Slimstat Analytics - Version 4.7.7

Version Description

  • [New] We've completely rewritten the portion of code that handles the date ranges in the Filter Bar. In order to simplify things, we have deprecated the interval_direction filter, which is now expressed by the sign in front of the interval value (positive for going forward from a given start date, and negative for going back in time). Please note that this change affect your existing shortcodes, if they use the aforementioned filter. We will update our documentation in the next few days to remove any reference to this filter, and to avoid any confusion. Please feel free to contact us if you have any questions or to report any issues.
  • [New] The comparison chart is now always displayed, using new criteria to determine the range to use. You may want to update your settings (Settings > Reports > Default Time Span > Days, and Reports > Comparison Chart) to mimic the old behavior or hide the comparison chart altogether, if you like.
  • [Update] We've reintroduced the various levels of granularity for our charts: hourly (when a single day is selected), daily (for ranges up to 120 days) and monthly. Also, the comparison chart is now always available, regardless of the selected time range. Thank you, WebsiteOpzetten.
  • [Update] Tooltips across the interface have a more uniform behavior.
Download this release

Release Info

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

Code changes from version 4.7.6.1 to 4.7.7

README.md DELETED
@@ -1,192 +0,0 @@
1
- # Slimstat Analytics
2
- **Contributors:** coolmann
3
- **Donate link:** https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BNJR5EZNY3W38
4
- **Tags:** analytics, statistics, counter, tracking, reports, wassup, geolocation, online users, spider, tracker, pageviews, stats, maxmind, statistics, statpress, power stats, hit
5
- Text Domain: wp-slimstat
6
- **Requires at least:** 3.8
7
- **Tested up to:** 4.9
8
- **Stable tag:** 4.7.6.1
9
-
10
-
11
- ## Description
12
- The leading web analytics plugin for WordPress. Track returning customers and registered users, monitor Javascript events, detect intrusions, analyze email campaigns. Thousands of WordPress sites are already using it.
13
-
14
-
15
- ### Feature Spotlight
16
- [youtube https://www.youtube.com/watch?v=zEKP9yC8x6g]
17
-
18
-
19
- ### Main features
20
- * Get access to real-time access log, measure server latency, track page events, keep an eye on your bounce rate and much more.
21
- * Add shortcodes to your website to display reports in widgets or directly in posts and pages.
22
- * Exclude users from statistics collection based on various criteria, including; user roles, common robots, IP subnets, admin pages, country, etc.
23
- * Export your reports to CSV, generate user heatmaps or get daily emails right in your mailbox (via premium add-ons).
24
- * Compatible with W3 Total Cache, WP SuperCache, CloudFlare and most caching plugins.
25
- * Support for hashing IP addresses in the database to protect your users privacy.
26
- * Accurate IP geolocation, browser and platform detection (courtesy of [MaxMind](http://www.maxmind.com/) and [Browscap](http://browscap.org)).
27
- * World Map that works on your mobile device, too (courtesy of [amMap](http://www.ammap.com/)).
28
-
29
-
30
- ### Premium Add-ons
31
- Visit [our website](http://www.wp-slimstat.com/addons/) for a list of available extensions.
32
-
33
-
34
- ### Social Media
35
- [Like Us](https://www.facebook.com/slimstatistics/) on Facebook and [follow us](https://twitter.com/wp_stats) on Twitter to get the latest news and updates about our plugin.
36
-
37
-
38
- ### Translations
39
- Slimstat is available in multiple languages: English, Belarusian (UStarCash), Chinese (沐熙工作室), Farsi, French (Michael Bastin, Jean-Michel Venet, Yves Pouplard, Henrick Kac), German (TechnoViel), Indonesian ([ChameleonJohn](https://www.chameleonjohn.com/)), Italian ([Slimstat Dev Team](https://www.wp-slimstat.com)), Japanese (h_a_l_f), Portuguese, Russian (Vitaly), Spanish ([WebHostingHub](http://www.webhostinghub.com/)), Swedish (Per Soderman) and Turkish (Seyit Mehmet Çoban). Is your language missing or incomplete? [Contact us](http://support.wp-slimstat.com/) today.
40
-
41
-
42
- ### Reviews and Feedback
43
- * This is by far the most accurate and in-depth tracking plugin I've encountered for WordPress [MiMango](https://wordpress.org/support/topic/excellent-plugin-and-service-9)
44
- * I have been relying on SlimStat to not only track all traffic to my sites accurately but also to present the stats in very useful graphic format [JJD3](https://wordpress.org/support/topic/an-essential-plugin-14)
45
- * 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)
46
- * 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)
47
- * Read all the [reviews](http://wordpress.org/support/view/plugin-reviews/wp-slimstat) and feel free to post your own!
48
-
49
-
50
- ### Requirements
51
- * WordPress 3.8+
52
- * PHP 5.2+ (or 5.5+ if you use the Browscap data file)
53
- * MySQL 5.0.3+
54
- * At least 20 MB of free web space
55
- * At least 5 MB of free DB space
56
- * At least 32 Mb of free PHP memory for the tracker (peak memory usage)
57
- * IE9+ or any browser supporting HTML5, to access the reports
58
-
59
-
60
- ## Installation
61
- 1. In your WordPress admin, go to Plugins > Add New
62
- 2. Search for WP Slimstat Analytics
63
- 3. Click on **Install Now** under WP Slimstat Analytics and then activate the plugin
64
- 4. Make sure your template calls `wp_footer()` or the equivalent hook somewhere (possibly just before the `</body>` tag)
65
- 5. Go to Slimstat > Settings > Maintenance tab > MaxMind IP to Country section and click on "Install GeoLite DB" to detect your visitors' countries based on their IP addresses
66
- 6. If your `wp-admin` folder is not publicly accessible, make sure to check the [FAQs](http://wordpress.org/extend/plugins/wp-slimstat/faq/) to see if there's anything else you need to do
67
-
68
-
69
- ## Please note
70
- * If you decide to uninstall Slimstat Analytics, all the stats will be **PERMANENTLY** deleted from your database. Make sure to setup a database backup (wp_slim_*) to avoid losing your data.
71
- * If you are upgrading from a version prior to 4.0, please install version 4.0 first to upgrade the database structure and download the new Geolocation data.
72
-
73
-
74
- ## Frequently Asked Questions
75
- Our knowledge base is available on our [support center](http://docs.wp-slimstat.com/) website.
76
-
77
-
78
- ## Screenshots
79
- ### 1. **Overview** - Your website traffic at a glance
80
- [missing image]
81
-
82
- ### 2. **Activity Log** - A real-time view of your visitors' whereabouts
83
- [missing image]
84
-
85
- ### 3. **Settings** - Plenty of options to customize the plugin's behavior
86
- [missing image]
87
-
88
- ### 4. **Interactive World Map** - See where your visitors are coming from
89
- [missing image]
90
-
91
- ### 5. **Responsive layout** - Keep an eye on your reports on the go
92
- [missing image]
93
-
94
-
95
-
96
- ## Changelog
97
-
98
- ### 4.7.6.1
99
- * [Fix] The new Javascript library was interfering with the dropdown menus on the WordPress Dashboard. Thanks to all of those who helped us troubleshoot the issue.
100
-
101
-
102
- ### 4.7.6
103
- * [Note] As we mentioned earlier, we've been working on streamlining and cleaning up our source code. It's incredible how layers of code can deposit on top of each other, until they form a thick layer that prevents developers from seeing clearly what's happening. It was time to apply some virtual citric acid to descale our code. If you're using any kind of server caching functionality, please make sure to clear your cache before opening a support request. Also, do not hesitate to reach out to us if you notice any strange or unusual behaviors.
104
- * [New] You can now download the geolocation map as a PDF file or PNG image. Thank you, Steve, for suggesting this feature!
105
- * [New] You can now exclude pageviews by content type, like 404, taxonomy, search, etc. Thank you, [victor50g](https://wordpress.org/support/topic/suggestion-enable-filtering-on-content-404/).
106
- * [Update] Deprecated the 'direction' filter, which was a leftover from over 5 years ago, when we used our old interface.
107
- * [Fix] The cron job to archive old records was not firing as expected under certain circumstances.
108
- * [Fix] Charts did not render correctly, if no data was available for that specific view.
109
-
110
-
111
- ### 4.7.5.3
112
- * [New] For those who can't manage to automatically update their local copy of our premium add-ons, we've added a link to manually download the zip file. You'll find it under the Plugins screen, once you've entered and saved the corresponding license key under Slimstat > Add-ons.
113
- * [Update] The [Update Checker](https://github.com/YahnisElsts/plugin-update-checker) library has been updated to version 4.4.
114
- * [Fix] The world map was not being displayed correctly if no data points were available for the selected time range (thank you, Michel).
115
-
116
-
117
- ### 4.7.5.2
118
- * [Update] You can now customize the amount of dots displayed on the World Map, under Slimstat > Settings > Reports > Access Log and World Map. Thank you, [service4](https://wordpress.org/support/topic/new-geolocation-map-with-cities/).
119
- * [Fix] A dependency error was being highlighted for one of our premium add-ons under certain circumstances. Thank you, Peter.
120
- * [Fix] The option to not set the session cookie was not working as expected. Thank you, [Bjarne](https://wordpress.org/support/topic/disable-cookies-2/#post-9887099).
121
-
122
-
123
- ### 4.7.5.1
124
- * [Update] Implemented a workaround to try and fix the "Forbidden" error that a few users are experiencing when trying to download the MaxMind Geolite2 data file.
125
- * [Fix] Updated the link to manually download the MaxMind data file from their servers, and added a new page to our knowledge base to explain how to manually install it.
126
-
127
-
128
- ### 4.7.5
129
- * [New] Now that Slimstat is capable of geolocating visitors at the city level, wouldn't it make sense to display those visitors on the map? Well, of course! Go check out this new feature by accessing the Geolocation tab in Slimstat.
130
- * [New] Updated the tracking script to handle events triggered by external libraries, like the [Vimeo API](https://github.com/vimeo/player.js/#events). Thank you, Max.
131
- * [New] Added new operator "included_in_set", which allows you to list multiple values to match against, when composing a shortcode.
132
- * [New] Added new option to avoid that Slimstat assigns a COOKIE to your visitors. Thank you, [dragon013](https://wordpress.org/support/topic/disable-cookies-2/).
133
- * [Fix] A bug was preventing the feature to "restrict users" to only see their reports from working as expected.
134
-
135
-
136
- ### 4.7.4.1
137
- * [Update] The Browscap data file is now loaded only when needed, thus removing its inherent overhead when unnecessary.
138
- * [Update] The Browscap data file has been updated to the latest version available on their repository (ver 6026).
139
- * [Fix] Addressed a remote XSS Vulnerability responsibly disclosed by one of our customers. Thank you, [riscybusiness](https://wordpress.org/support/topic/security-vulnerability-affecting-slimstat/).
140
- * [Fix] Reintroduced the WHOIS pin, which has been removed by mistake because of a regression bug. Thank you, [brachialis](https://wordpress.org/support/topic/whois-location-icon-disappear/).
141
-
142
-
143
- ### 4.7.4
144
- * [Update] New fields added to the Email Report and Export to Excel add-ons, by extending how certain reports are defined in core.
145
- * [Fix] The [false positive](https://www.virustotal.com/#/file/43f69d9c4028f857b5b5544ea4559c03b4d58e02d75617482db517c626164363/detection) alert related to a virus in our code was fixed by updating [AmChart](https://www.amcharts.com/) to the latest version available (thank you, Sasa).
146
- * [Fix] Removed a PHP warning of undefined index (thank you, [slewis1000 and Sasa](https://wordpress.org/support/topic/php-notice-undefined-index-country/))
147
- * [Fix] The MozScape report was causing connectivity issues for some users, and it is now set as "hidden" by default.
148
- * [Fix] Regression bug related to our Export to Excel add-on.
149
-
150
-
151
- ### 4.7.3.1
152
- * [Fix] Apparently more people than we initially thought have issues with the MaxMind data file not being saved as expected. We are introducing a temporary fix while we try to investigate this issue further.
153
-
154
-
155
- ### 4.7.3
156
- * [Fix] A [few users](https://wordpress.org/support/topic/cannot-install-maxmind-geolite-db/) pointed out a weird behavior when installing the MaxMind Geolocation data file, where an empty folder would be created instead of the actual file. If you still experience issues related to this problem, please make sure to delete the empty folder "maxmind.mmdb" under `wp-content/uploads/wp-slimstat/`.
157
- * [Fix] Apparently Microsoft Security Essentials [was not pleased with our code](https://wordpress.org/support/topic/trojandownloader097m-donoff-detected-in-archive/), and was returning a false positive alert that a virus was included with the source code (thank you, Sasa).
158
- * [Fix] The "content_id" filter could not be used in a shortcode to reference other pages (i.e. `[slimstat f='count-all' w='id']content_id equals 2012[/slimstat]`). Thank you, Felipe.
159
- * [Fix] Country flags were not being displayed properly under certain circumstances (thank you, [Catmaniax](https://wordpress.org/support/topic/minor-issue-missing-png-file/)).
160
- * [Fix] Bug preventing the new Heatmap Add-on from working as expected.
161
-
162
-
163
- ### 4.7.2.2
164
- * [New] Added support for SCRIPT_DEBUG: by defining this constant in your `wp-config.php` will make Slimstat load the unminified version of the javascript tracker (thank you, Sasa)
165
- * [Update] Added new parameter to make the `admin-ajax.php` URL relative, to solve issues like [this one](https://wordpress.org/support/topic/xmlhttprequest-cannot-load-wp-adminadmin-ajax-php-3/).
166
- * [Fix] The Network Settings premium add-on was not working because of a bug in the main plugin. Thank you, Steve, for pointing us into the right direction.
167
- * [Fix] Updated the schema (columns) for the archive table.
168
-
169
-
170
-
171
- ### 4.7.2.1
172
- * [Fix] The new table columns "location" and "city" were not being created on a fresh install (thank you, [nielsgoossens](https://wordpress.org/support/topic/no-data-anymore-2/#post-9491034))
173
- * [Fix] Async mode was not working as expected (thank you, [keithgbcc](https://wordpress.org/support/topic/doesnt-work-1694/#post-9487448))
174
-
175
-
176
- ### 4.7.2
177
- * [New] As those who have been using Slimstat for a while know, we never stop doing our good share of research and development to improve our plugin. One feature on our wishlist was to make the geolocation functionality more accurate. Specifically, users have been asking us to track not just the Country of origin, but possibly the state and city. In order to geolocate visitors, our code has been leveraging a third-party data file provided by [MaxMind.com](https://www.maxmind.com/en/home). A while ago, they launched a new data format, which improves performance and offers a way to quickly determine the city of origin. However, the new library required a higher version of PHP, and up until now we had been hesitant to adopt it, to allow more people to use our plugin, over the chance of offering this feature. Now, after spending some time combing through their code, we found a way to get the best of both worlds: by customizing their PHP library, we were able to make it work with PHP 5.3! Which means that now Slimstat is able to tell you your visitors' city of origin (and State, when applicable) right out of the box. This information is available in the Access Log report and in a new 'Top Cities' report under the Audience tab. Please note: the MaxMind data file to enable this feature is approximately 60 Mb, and for this reason <strong>this new functionality is not enabled by default</strong>. You must go to Slimstat > Settings > Tracker and turn on the corresponding option. Then go to Slimstat > Settings > Maintenance and uninstall/install the GeoLite file to download the one that contains the city data. Please feel free to contact us if you have any questions.
178
- * [Update] Removed backward compatibility code for those updating from a version prior to 4.2. Hopefully most of our users are using a newer version that that. If you're not, please contact our support service for instructions on how to upgrade.
179
- * [Update] The format used to save your settings in the database has been changed. You MUST update your premium add-ons as soon as possible, and get the version compatible with this new format, or you might notice unexpected behaviors. Please contact us if you experience difficulties updating your add-ons.
180
- * [Update] Cleaned up some old CSS code affecting the reports.
181
-
182
-
183
- ### 4.7.1
184
- * [Fix] The new feature introduced in version 4.6.9.1 to allow our users to customize the default time range for the reports, had introduced a regression bug. Thank you to all our users who volunteered to test the bugfix.
185
- * [Fix] A vulnerability has been disclosed by [Pluginvulnerabilities.com](pluginvulnerabilities.com): an attacker with admin credentials could leverage the import/export mechanism for the plugin's settings to inject some malicious code. We recommend that you upgrade to the latest version of Slimstat as soon as possible.
186
- * [Fix] The new version of the [Add-on Update Checker library](https://github.com/YahnisElsts/plugin-update-checker), bundled with the previous release, was returning a fatal error under certain circumstances (thank you, Pepe).
187
-
188
-
189
- ## Support Our Work
190
- Slimstat Analytics is an open source project, dependent in large parts on donations. [This page](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BNJR5EZNY3W38)
191
- is for those who want to donate money - be it once, be it regularly, be it a small or a big amount. Everything is set up for an easy donation process.
192
- Try it out, you'll be amazed how good it feels! If you're on a tight budget, please consider writing [a review](https://wordpress.org/support/plugin/wp-slimstat/reviews/#new-post) for Slimstat as a token of appreciation for our hard work!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/config/index.php CHANGED
@@ -1,245 +1,246 @@
1
- <?php
2
-
3
- // Avoid direct access to this piece of code
4
- if ( !function_exists( 'add_action' ) ) {
5
- exit(0);
6
- }
7
-
8
- if ( isset( $_POST[ 'options' ][ 'ignore_capabilities' ] ) ) {
9
- // Make sure all the capabilities exist in the system
10
- $capability_not_found = false;
11
- foreach( wp_slimstat::string_to_array( $_POST[ 'options' ][ 'ignore_capabilities' ] ) as $a_capability ) {
12
- if ( isset( $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) && !array_key_exists( $a_capability, $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) ) {
13
- $capability_not_found = true;
14
- break;
15
- }
16
- }
17
-
18
- if ( !$capability_not_found ) {
19
- wp_slimstat::$settings[ 'ignore_capabilities' ] = $_POST[ 'options' ][ 'ignore_capabilities' ];
20
- }
21
- else{
22
- wp_slimstat_admin::$faulty_fields[] = __( 'Invalid capability. Please check <a href="http://codex.wordpress.org/Roles_and_Capabilities" target="_new">this page</a> for more information', 'wp-slimstat' );
23
- }
24
- }
25
-
26
- if ( isset( $_POST[ 'options' ][ 'can_view' ] ) ) {
27
- // Make sure all the users exist in the system
28
- $post_data = trim( $_POST[ 'options' ][ 'can_view' ] );
29
- $user_array = wp_slimstat::string_to_array( $_POST[ 'options' ][ 'can_view' ] );
30
-
31
- if ( !empty( $post_data ) ) {
32
- $sql_user_placeholders = implode( ', ', array_fill( 0, count( $user_array ), '%s' ) );
33
- if ( $GLOBALS[ 'wpdb' ]->get_var( $GLOBALS[ 'wpdb' ]->prepare( "SELECT COUNT( * ) FROM {$GLOBALS[ 'wpdb' ]->users} WHERE user_login IN ( $sql_user_placeholders )", $user_array ) ) == count( $user_array ) ) {
34
- wp_slimstat::$settings[ 'can_view' ] = $_POST[ 'options' ][ 'can_view' ];
35
- }
36
- else{
37
- wp_slimstat_admin::$faulty_fields[] = __( 'Read access: username not found', 'wp-slimstat' );
38
- }
39
- }
40
- else {
41
- wp_slimstat::$settings[ 'can_view' ] = '';
42
- }
43
- }
44
-
45
- if ( isset( $_POST[ 'options' ][ 'capability_can_view' ] ) ) {
46
- if ( isset( $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) && array_key_exists( $_POST[ 'options' ][ 'capability_can_view' ], $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) ) {
47
- wp_slimstat::$settings[ 'capability_can_view' ] = $_POST[ 'options' ][ 'capability_can_view' ];
48
- }
49
- else{
50
- wp_slimstat_admin::$faulty_fields[] = __( 'Invalid capability. Please check <a href="http://codex.wordpress.org/Roles_and_Capabilities" target="_new">this page</a> for more information', 'wp-slimstat' );
51
- }
52
- }
53
-
54
- if ( isset( $_POST[ 'options' ][ 'can_admin' ] ) ) {
55
- // Make sure all the users exist in the system
56
- $post_data = trim( $_POST[ 'options' ][ 'can_admin' ] );
57
- $user_array = wp_slimstat::string_to_array( $_POST[ 'options' ][ 'can_admin' ] );
58
-
59
- if ( is_array( $user_array ) && !empty( $post_data ) ) {
60
- $sql_user_placeholders = implode( ', ', array_fill( 0, count( $user_array ), '%s' ) );
61
- if ( $GLOBALS[ 'wpdb' ]->get_var( $GLOBALS[ 'wpdb' ]->prepare( "SELECT COUNT( * ) FROM {$GLOBALS[ 'wpdb' ]->users} WHERE user_login IN ( $sql_user_placeholders )", $user_array ) ) == count( $user_array ) ) {
62
- wp_slimstat::$settings[ 'can_admin' ] = $_POST[ 'options' ][ 'can_admin' ];
63
- }
64
- else{
65
- wp_slimstat_admin::$faulty_fields[] = __( 'Config access: username not found', 'wp-slimstat' );
66
- }
67
- }
68
- }
69
-
70
- if ( isset( $_POST[ 'options' ][ 'capability_can_admin' ] ) ) {
71
- if ( isset( $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) && array_key_exists( $_POST[ 'options' ][ 'capability_can_admin' ], $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) ) {
72
- wp_slimstat::$settings[ 'capability_can_admin' ] = $_POST[ 'options' ][ 'capability_can_admin' ];
73
- }
74
- else{
75
- wp_slimstat_admin::$faulty_fields[] = __( 'Invalid capability. Please check <a href="http://codex.wordpress.org/Roles_and_Capabilities" target="_new">this page</a> for more information', 'wp-slimstat' );
76
- }
77
- }
78
-
79
- $current_tab = empty( $_GET[ 'tab' ] ) ? 1 : intval( $_GET[ 'tab' ] );
80
-
81
- // Define all the options
82
- $settings = array(
83
- 1 => array(
84
- 'title' => __( 'Basic', 'wp-slimstat' ),
85
- 'rows' => array(
86
- 'general_tracking_header' => array( 'description' => __( 'Tracker', 'wp-slimstat' ), 'type' => 'section_header' ),
87
- 'is_tracking' => array( 'description' => __( 'Enable Tracking', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Turn the tracker on or off, while keeping the reports accessible.', 'wp-slimstat' ) ),
88
- 'javascript_mode' => array( 'description' => __( 'Tracking Mode', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __('Select <strong>Client</strong> if you are using a caching plugin (W3 Total Cache, WP SuperCache, HyperCache, etc). Slimstat will behave pretty much like Google Analytics, and visitors whose browser does not support Javascript will be ignored. A nice side effect is that <strong>most spammers, search engines and other crawlers</strong> will not be tracked.','wp-slimstat'), 'custom_label_on' => __('Client','wp-slimstat'), 'custom_label_off' => __('Server','wp-slimstat') ),
89
- 'enable_javascript' => array( 'description' => __( 'Track Client Info', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "If this option is turned off, the tracker will not be collecting information such as screen resolution, outbound links, downloads, etc. This option is ignored if Tracking Mode is set to Client.", 'wp-slimstat' ) ),
90
- 'track_admin_pages' => array( 'description' => __( 'Track Admin Pages', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Enable this option to track your users' activity within the admin.", 'wp-slimstat') ),
91
-
92
- 'general_integration_header' => array( 'description' => __( 'WordPress Integration', 'wp-slimstat' ), 'type' => 'section_header' ),
93
- 'add_dashboard_widgets' => array( 'description' => __( 'Dashboard Widgets', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Enable this option if you want to add reports to your WordPress Dashboard. Use the Customizer to choose which ones to display.', 'wp-slimstat' ) ),
94
- 'use_separate_menu' => array( 'description' => __( 'Menu Position', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Choose between a standalone admin menu for Slimstat or a drop down in the admin bar (if visible).', 'wp-slimstat' ), 'custom_label_on' => __( 'Side', 'wp-slimstat' ), 'custom_label_off' => __( 'Bar', 'wp-slimstat' ) ),
95
- 'posts_column_day_interval' => array( 'description' => __('Report Interval','wp-slimstat'), 'type' => 'integer', 'long_description' => __('Enter the time range, in days, that should be used to count the number of pageviews on Posts/Pages (see option here below) and for the Default Time Span option under the Reports tab.','wp-slimstat') ),
96
- 'add_posts_column' => array( 'description' => __('Posts and Pages','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('Add a new column to the Edit Posts/Pages screens, with the number of hits per post within the timeframe specified here below.','wp-slimstat') ),
97
-
98
- 'posts_column_pageviews' => array( 'description' => __( 'Report Type','wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Select what kind of information you would like to see displayed on your Posts admin screen. Hits counts all the pageviews regardless of the user, (unique) IPs counts only one hit per IP in the given time range.', 'wp-slimstat' ), 'custom_label_on' => __( 'Hits', 'wp-slimstat' ), 'custom_label_off' => __( 'IPs', 'wp-slimstat' ) ),
99
- 'hide_addons' => array( 'description' => __( 'Hide Add-ons', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Enable this option to hide all your <strong>active</strong> premium add-ons from the list of plugins in WordPress. Please note that you will still receive updates for hidden add-ons.', 'wp-slimstat' ) ),
100
-
101
- 'general_database_header' => array('description' => __('Database','wp-slimstat'), 'type' => 'section_header'),
102
- '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 in the main table (neither archive it nor delete it) regardless of its age.", 'wp-slimstat' ).( (wp_slimstat::$settings[ 'auto_purge' ] > 0)?' '.__('Next clean-up on','wp-slimstat').' <strong>'.date_i18n(get_option('date_format').', '.get_option('time_format'), wp_next_scheduled( 'wp_update_plugins' ) ) . '</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::$settings['auto_purge'].' days'))):''), 'after_input_field' => __('days','wp-slimstat') ),
103
- 'auto_purge_delete' => array( 'description' => __( 'Archive records', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'If DB space is not an issue, you can decide to archive older records in another table, instead of deleting them. This way performance is preserved, but you will still be able to access your data at a later time, if needed. Please note that the archive table (<code>wp_slim_stats_archive</code>) will be <strong>deleted</strong> along with all the other tables, when Slimstat is uninstalled. Make sure to backup your data before you proceed.', 'wp-slimstat' ) )
104
- )
105
- ),
106
-
107
- 2 => array(
108
- 'title' => __( 'Tracker', 'wp-slimstat' ),
109
- 'rows' => array(
110
- 'filters_outbound_header' => array( 'description' => __( 'Link Tracking', 'wp-slimstat' ), 'type' => 'section_header' ),
111
- 'do_not_track_outbound_classes_rel_href' => array( 'description' => __( 'Do Not Track', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Slimstat will ignore links marked with one of these class names, <em>rel</em> attributes or whose <em>href</em> attribute contains one of these strings. Please keep in mind that the class <code>noslimstat</code> is also used to avoid tracking interactive links throughout the reports. If you remove it from this list, some features might not work as expected.", 'wp-slimstat' ) ),
112
- 'extensions_to_track' => array( 'description' => __( 'Downloads', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( 'List all the file extensions that you want to be treated as Downloads. Please note that links pointing to external resources (i.e. PDFs on an external website) are considered Downloads and not Outbound Links (and tracked as such), if their extension matches one of the ones listed here below.', 'wp-slimstat' ) ),
113
- 'track_same_domain_referers' => array( 'description' => __( 'Same-Domain Referrers', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'By default, when the domain of the referrer for a given page view is the same as the current site, that information is not tracked to save space in the database. However, if you are running a multisite network with subfolders, you might need to track same-domain referrers from one site to another, as they are technically "separate" websites.', 'wp-slimstat' ) ),
114
-
115
- 'advanced_tracker_header' => array( 'description' => __( 'Advanced Options', 'wp-slimstat' ), 'type' => 'section_header' ),
116
- 'geolocation_country' => array( 'description' => __( 'Geolocation Precision', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "When Slimstat determines your visitors' Country of origin, it uses a third-party data file <a href='https://dev.maxmind.com/geoip/geoip2/geolite2/' target='_blank'>provided by MaxMind</a>. They offer two precision levels: country and city. By default, Slimstat will install the smaller one (country), and you can decide to use the other one, if you don't mind its 60 Mb average size. After you change this option, please <strong>go to the Maintenance tab</strong> and reload (uninstall/install) the MaxMind GeoLite DB by clicking on the corresponding button.", 'wp-slimstat' ), 'custom_label_on' => __( 'Country', 'wp-slimstat' ), 'custom_label_off' => __( 'City', 'wp-slimstat' ) ),
117
- 'session_duration' => array('description' => __( 'Session Duration', 'wp-slimstat' ), 'type' => 'integer', 'long_description' => __( 'How many seconds should a human session last? Google Analytics sets it to 1800 seconds.', 'wp-slimstat' ), 'after_input_field' => __( 'seconds', 'wp-slimstat' ) ),
118
- 'extend_session' => array( 'description' => __( 'Extend Session', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Extend the duration of a session each time the user visits a new page.', 'wp-slimstat' ) ),
119
- 'set_tracker_cookie' => array( 'description' => __( 'Set Cookie', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Disable this option if, for legal or security reasons, you do not want Slimstat to assign a <a href="https://en.wikipedia.org/wiki/HTTP_cookie" target="_blank">cookie</a> to your visitors. Please note that, by deactivating this feature, Slimstat will not keep track of returning visitors and sessions.', 'wp-slimstat' ) ),
120
- 'enable_cdn' => array( 'description' => __( 'Enable CDN', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Use <a href='http://www.jsdelivr.com/' target='_blank'>JSDelivr</a>'s CDN, by serving our tracking code from their fast and reliable network (free service).", 'wp-slimstat' ) ),
121
- 'ajax_relative_path' => array( 'description' => __( 'Relative Ajax', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'If you are experiencing issues related to the header field X-Requested-With not being allowed by Access-Control-Allow-Headers in preflight response (or similar), try enabling this option to make that <code>admin-ajax.php</code> URL relative.', 'wp-slimstat' ) ),
122
-
123
- 'advanced_external_pages_header' => array('description' => __('External Pages','wp-slimstat'), 'type' => 'section_header'),
124
- 'external_pages_script' => array('type' => 'static', 'skip_update' => 'yes', 'description' => __('Add the following code to all the non-WP pages you want to track, right before the closing BODY tag. Please make sure to change the protocol of all the URLs to HTTPS, if you external site is served over a secure channel.','wp-slimstat'), 'long_description' => '&lt;script type="text/javascript"&gt;
125
- /* &lt;![CDATA[ */
126
- var SlimStatParams = {
127
- ajaxurl: "'.admin_url('admin-ajax.php').'",
128
- ci: "YTo0OntzOjEyOiJjb250ZW50X3R5cGUiO3M6ODoiZXh0ZXJuYWwiO3M6ODoiY2F0ZWdvcnkiO3M6MDoiIjtzOjEwOiJjb250ZW50X2lkIjtpOjA7czo2OiJhdXRob3IiO3M6MTM6ImV4dGVybmFsLXBhZ2UiO30=.' . md5('YTo0OntzOjEyOiJjb250ZW50X3R5cGUiO3M6ODoiZXh0ZXJuYWwiO3M6ODoiY2F0ZWdvcnkiO3M6MDoiIjtzOjEwOiJjb250ZW50X2lkIjtpOjA7czo2OiJhdXRob3IiO3M6MTM6ImV4dGVybmFsLXBhZ2UiO30=' . wp_slimstat::$settings[ 'secret' ] ).'",
129
- extensions_to_track: "'.wp_slimstat::$settings['extensions_to_track'].'"
130
- };
131
- /* ]]&gt; */
132
- &lt;/script&gt;
133
- &lt;script type="text/javascript" src="http://cdn.jsdelivr.net/wp/wp-slimstat/trunk/wp-slimstat.min.js"&gt;&lt;/script&gt;', 'use_tag_list' => false ),
134
- 'external_domains' => array('description' => __('Allow Domains','wp-slimstat'), 'type' => 'textarea', 'long_description' => __("If you are getting an error saying that no 'Access-Control-Allow-Origin' header is present on the requested resource, when using the external tracking code here above, list the domains (complete with scheme) you would like to allow. For example: <code>http://my.domain.ext</code> (no trailing slash). Please see <a href='http://www.w3.org/TR/cors/#security' target='_blank'>this W3 resource</a> for more information on the security implications of allowing CORS requests.",'wp-slimstat'))
135
- )
136
- ),
137
-
138
- 3 => array(
139
- 'title' => __( 'Filters', 'wp-slimstat' ),
140
- 'rows' => array(
141
- 'filters_categories_header' => array( 'description' => __( 'Profiling', 'wp-slimstat' ), 'type' => 'section_header' ),
142
- 'track_users' => array( 'description' => __( 'Track WP Users', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Enable this option to track logged in users.', 'wp-slimstat' ) ),
143
- 'ignore_spammers' => array( 'description' => __( 'Ignore Spammers', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Enable this option if you don't want to track visits from users identified as spammers by third-party tools like Akismet. Pageviews generated by users whose comments are later marked as spam, will also be removed from the database.", 'wp-slimstat' ) ),
144
- 'ignore_bots' => array( 'description' => __( 'Ignore Bots', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Turn on this feature if you want to have the accuracy level of server-side tracking, but not the inconvenience of getting your database clogged with pageviews generated by crawlers, spiders, search engine bots, etc. Please note that in Client mode, bots are ignored regardless of this setting.", 'wp-slimstat' ) ),
145
- 'ignore_prefetch' => array( 'description' => __( 'Ignore Prefetch Requests', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Prevent Slimstat from tracking pageviews generated by Firefox's <a href='https://developer.mozilla.org/en/Link_prefetching_FAQ' target='_blank'>Link Prefetching functionality</a>.", 'wp-slimstat' ) ),
146
- 'anonymize_ip' => array( 'description' => __( 'Enable Privacy Mode', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Mask your visitors' IP addresses to comply with European Privacy Laws.", 'wp-slimstat' ) ),
147
-
148
- 'filters_users_header' => array( 'description' => __( 'User Properties', 'wp-slimstat' ), 'type' => 'section_header' ),
149
- 'ignore_users' => array( 'description' => __( 'Usernames', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "List all the usernames you don't want to track. Please be aware that spaces are <em>not</em> ignored and that usernames are case sensitive. Wildcards: <code>*</code> matches 'any string, including the empty string', <code>!</code> matches 'any character'. For example, <code>user*</code> will match user12 and userfoo, <code>u*100</code> will match user100 and uber100, <code>user!0</code> will match user10 and user90.", 'wp-slimstat' ) ),
150
- 'ignore_ip' => array( 'description' => __( 'IP Addresses', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "List all the IP addresses you don't want to track. Each subnet <strong>must</strong> be defined using the <a href='https://www.iplocation.net/subnet-mask' target='_blank'>CIDR notation</a> (i.e. <em>192.168.0.0/24</em>). This filter applies both to the public IP and the originating IP, if available. Using the CIDR notation, you will use octets to determine the subnet, so for example 54.0.0.0/8 means that the first number is represented by 8 bits, hence 8 after the slash. Then the second number would be another 8 bits, so you would write 54.12.0.0/16 (16 = 8 + 8), and you could do the same for the third number, for example 54.12.34.0/24 (24 = 8 + 8 + 8).", 'wp-slimstat' ) ),
151
- 'ignore_countries' => array( 'description' => __( 'Countries', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Country codes (i.e.: <code>en-us, it, es</code>) that you don't want to track.", 'wp-slimstat' ) ),
152
- 'ignore_browsers' => array('description' => __( 'User Agents', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Browsers (user agents) you don't want to track. You can specify the browser's version adding a slash after the name (i.e. <em>Firefox/3.6</em>). Wildcards: <code>*</code> matches 'any string, including the empty string', <code>!</code> matches 'any character'. For example, <code>Chr*</code> will match Chrome and Chromium, <code>IE/!.0</code> will match IE/7.0 and IE/8.0. Strings are case-insensitive.", 'wp-slimstat' ) ),
153
- 'ignore_platforms' => array( 'description' => __( 'Operating Systems', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Operating system codes you don't want to track. Please refer to <a href='https://slimstat.freshdesk.com/solution/articles/12000031504-what-are-the-operating-system-codes-used-by-slimstat-' target='_blank'>this page</a> in our knowledge base to learn more about what codes you can use. Usual rules for using wildcards apply (see fields here above).", 'wp-slimstat' ) ),
154
- 'ignore_capabilities' => array( 'description' => __( 'Capabilities', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Users having at least one of the <a href='http://codex.wordpress.org/Roles_and_Capabilities' target='_new'>capabilities</a> listed here below will not be tracked. Capabilities are case-insensitive.", 'wp-slimstat' ), 'skip_update' => true ),
155
-
156
- 'filters_pageview_header' => array( 'description' => __( 'Page Properties', 'wp-slimstat' ), 'type' => 'section_header' ),
157
- 'ignore_resources' => array( 'description' => __( 'Permalinks', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "List all the URLs on your website that you don't want to track. Don't include the domain name: <em>/about, ?p=1</em>, etc. Wildcards: <code>*</code> matches 'any string, including the empty string', <code>!</code> matches 'any character'. For example, <code>/abou*</code> will match /about and /abound, <code>/abo*t</code> will match /aboundant and /about, <code>/abo!t</code> will match /about and /abort. Strings are case-insensitive.", 'wp-slimstat' ) ),
158
- 'ignore_referers' => array( 'description' => __( 'Referring Sites', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Referring URLs that you don't want to track: <code>http://mysite.com*</code>, <code>*/ignore-me-please</code>, etc. Wildcards: <code>*</code> matches 'any string, including the empty string', <code>!</code> matches 'any character'. Strings are case-insensitive. Please include either a wildcard or the protocol you want to filter (http://, https://).", 'wp-slimstat' ) ),
159
- 'ignore_content_types' => array( 'description' => __( 'Content Types', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Slimstat categorizes pageviews by the associated WordPress content type: post, page, attachment, tag, 404, taxonomy, author, archive, search, feed, login and others. You can use this field to avoid tracking pages whose content type matches the ones you set here below.", 'wp-slimstat' ) )
160
- )
161
- ),
162
-
163
- 4 => array(
164
- 'title' => __( 'Reports', 'wp-slimstat' ),
165
- 'rows' => array(
166
- 'reports_basic_header' => array( 'description' => __( 'Data Formats and Conversion', 'wp-slimstat' ), 'type' => 'section_header' ),
167
- 'use_european_separators' => array( 'description' => __( 'Number Format', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Choose the number format you want to use for your reports.','wp-slimstat' ), 'custom_label_on' => '1.234,5', 'custom_label_off' => '1,234.5' ),
168
- 'date_format' => array('description' => __('Date Format','wp-slimstat'), 'type' => 'text', 'long_description' => __("<a href='http://php.net/manual/en/function.date.php' target='_blank'>PHP Format</a> to use when displaying a pageview's date.", 'wp-slimstat')),
169
- 'time_format' => array('description' => __('Time Format','wp-slimstat'), 'type' => 'text', 'long_description' => __("<a href='http://php.net/manual/en/function.date.php' target='_blank'>PHP Format</a> to use when displaying a pageview's time.", 'wp-slimstat')),
170
- 'show_display_name' => array('description' => __('Use Display Name','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('By default, users are listed by their usernames. Use this option to visualize their display names instead.','wp-slimstat')),
171
- 'convert_resource_urls_to_titles' => array('description' => __('Use Titles','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('Slimstat converts your permalinks into post, page and category titles. Disable this feature if you need to see the URL in your reports.', 'wp-slimstat')),
172
- 'convert_ip_addresses' => array('description' => __('Convert IP Addresses','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('Display provider names instead of IP addresses.','wp-slimstat')),
173
-
174
- 'reports_functionality_header' => array( 'description' => __( 'Functionality', 'wp-slimstat' ), 'type' => 'section_header' ),
175
- 'async_load' => array( 'description' => __( 'Async Mode', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Activate this feature if your reports take a while to load. It breaks down the load on your server into multiple requests, thus avoiding memory issues and performance problems.', 'wp-slimstat' ) ),
176
- 'use_current_month_timespan' => array( 'description' => __( 'Default Time Span', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Determine what is the default time period for calculating all the data in each report: current month or past given number of days. The number of days is defined under Basic > Report Interval. You can always use the time filter dropdown to customize this value even further.', 'wp-slimstat' ), 'custom_label_on' => 'Month', 'custom_label_off' => 'Days' ),
177
- 'expand_details' => array('description' => __('Expand Details','wp-slimstat'), 'type' => 'toggle', 'long_description' => __("Expand each row's details by default, insted of on mousehover.",'wp-slimstat')),
178
- 'rows_to_show' => array('description' => __('Rows to Display','wp-slimstat'), 'type' => 'integer', 'long_description' => __('Specify the number of items in each report.','wp-slimstat')),
179
- 'limit_results' => array( 'description' => __( 'Max Results','wp-slimstat' ), 'type' => 'integer', 'long_description' => __( 'Decide how many records should be retrieved from the database in total. Depending on your server configuration, you may want to fine tune this value to avoid exceeding your PHP memory limit.', 'wp-slimstat' ) ),
180
- 'ip_lookup_service' => array('description' => __( 'IP Lookup', 'wp-slimstat' ), 'type' => 'text', 'long_description' => __( 'Customize the Geolocation service to be used in the reports. Default: <code>http://www.infosniper.net/?ip_address=</code>', 'wp-slimstat' ) ),
181
- 'mozcom_access_id' => array('description' => __( 'Mozscape Access ID', 'wp-slimstat' ), 'type' => 'text', 'long_description' => __( 'Get accurate rankings for your website through the free <a href="https://moz.com/community/join?redirect=/products/api/keys" target="_blank">Mozscape API</a> service. Sign up for a free community account to get started. Then enter your personal identification code here.', 'wp-slimstat' ) ),
182
- 'mozcom_secret_key' => array('description' => __( 'Mozscape Secret Key', 'wp-slimstat' ), 'type' => 'text', 'long_description' => __( 'Do not share your secret key with anyone or they will be able to make API requests on your account!', 'wp-slimstat' ) ),
183
-
184
- 'reports_right_now_header' => array( 'description' => __( 'Access Log and World Map', 'wp-slimstat' ), 'type' => 'section_header' ),
185
- 'refresh_interval' => array( 'description' => __( 'Auto Refresh', 'wp-slimstat' ), 'type' => 'integer', 'long_description' => __( 'Enable the Live View, which refreshes the Access Log every X seconds. Enter <strong>0</strong> (number zero) to deactivate this feature.', 'wp-slimstat' ), 'after_input_field' => __( 'seconds', 'wp-slimstat' ) ),
186
- 'number_results_raw_data' => array('description' => __( 'Rows to Display', 'wp-slimstat'), 'type' => 'integer', 'long_description' => __( 'Specify the number of items in the Access Log.', 'wp-slimstat' ) ),
187
- 'max_dots_on_map' => array('description' => __( 'Map Data Points', 'wp-slimstat'), 'type' => 'integer', 'long_description' => __( 'Customize the maximum number of dots displayed on the world map. Please note that large numbers might increase the amount of time needed to render the map.', 'wp-slimstat' ) ),
188
-
189
- 'reports_miscellaneous_header' => array('description' => __('Miscellaneous','wp-slimstat'), 'type' => 'section_header'),
190
- 'custom_css' => array( 'description' => __( 'Custom CSS', 'wp-slimstat' ), 'type' => 'textarea', 'rows' => 8, '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' ), 'use_tag_list' => false ),
191
- 'chart_colors' => array('description' => __('Chart Colors','wp-slimstat'), 'type' => 'textarea', 'long_description' => __("Customize the look and feel of your charts by assigning personalized colors to each metric. List 4 hex colors, strictly in the following order: metric 1 previous, metric 2 previous, metric 1 current, metric 2 current. For example: <code>#ccc, #999, #bbcc44, #21759b</code>.",'wp-slimstat')),
192
- 'show_complete_user_agent_tooltip' => array('description' => __('Show User Agent','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('Choose if you want to see the browser name or a complete user agent string when hovering on browser icons.','wp-slimstat')),
193
- 'enable_sov' => array('description' => __('Enable SOV','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('In linguistic typology, a subject-object-verb (SOV) language is one in which the subject, object, and verb of a sentence appear in that order, like in Japanese.','wp-slimstat'))
194
- )
195
- ),
196
-
197
- 5 => array(
198
- 'title' => __( 'Access Control', 'wp-slimstat' ),
199
- 'rows' => array(
200
- 'permissions_reports_header' => array('description' => __('Reports','wp-slimstat'), 'type' => 'section_header'),
201
- 'restrict_authors_view' => array('description' => __('Restrict Authors','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('Enable this option if you want your authors to only see stats related to their own content.','wp-slimstat')),
202
- 'capability_can_view' => array('description' => __('Capability','wp-slimstat'), 'type' => 'text', 'long_description' => __("Specify the minimum <a href='http://codex.wordpress.org/Roles_and_Capabilities' target='_new'>capability</a> needed to access the reports (default: <code>activate_plugins</code>). If this field is empty, <strong>all your users</strong> (including subscribers) will have access to the reports, unless a 'Read access' whitelist has been specified here below. In this case, the list has precedence over the capability.",'wp-slimstat')),
203
- 'can_view' => array('description' => __('Whitelist','wp-slimstat'), 'type' => 'textarea', 'long_description' => __("List all the users who should have access to the reports. Administrators are implicitly allowed, so you don't need to list them in here. Usernames are case sensitive.",'wp-slimstat'), 'skip_update' => true),
204
-
205
- 'permissions_config_header' => array('description' => __('Settings','wp-slimstat'), 'type' => 'section_header'),
206
- 'capability_can_admin' => array('description' => __('Capability','wp-slimstat'), 'type' => 'text', 'long_description' => __("Specify the minimum <a href='http://codex.wordpress.org/Roles_and_Capabilities' target='_new'>capability</a> required to configure Slimstat (default: <code>activate_plugins</code>). The whitelist here below can be used to override this option for specific users.",'wp-slimstat')),
207
- 'can_admin' => array('description' => __('Whitelist','wp-slimstat'), 'type' => 'textarea', 'long_description' => __("List all the users who can edit these options. Please be advised that admins <strong>are not</strong> implicitly allowed, so do not forget to include yourself! Usernames are case sensitive.",'wp-slimstat'), 'skip_update' => true),
208
-
209
- 'rest_api_header' => array( 'description' => __( 'Rest API', 'wp-slimstat' ), 'type' => 'section_header' ),
210
- 'rest_api_tokens' => array( 'description' => __( 'Tokens', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "In order to send requests to <a href='https://slimstat.freshdesk.com/support/solutions/articles/12000033661-slimstat-rest-api' target='_blank'>the Slimstat REST API</a>, you will need to pass a valid token to the endpoint (param ?token=XXX). Using the field here below, you can define as many tokens as you like, to distribute them to your API users. Please note: treat these tokens as passwords, as they will grant read access to your reports to anyone who knows them. Use a service like <a href='https://randomkeygen.com/#ci_key' target='_blank'>RandomKeyGen.com</a> to generate unique secure tokens.", 'wp-slimstat' ) )
211
- )
212
- ),
213
-
214
- 6 => array(
215
- 'title' => __( 'Maintenance', 'wp-slimstat' ),
216
- 'include' => dirname(__FILE__).'/maintenance.php'
217
- ),
218
-
219
- 7 => array(
220
- 'title' => __( 'Add-ons', 'wp-slimstat' )
221
- )
222
- );
223
-
224
- $settings = apply_filters( 'slimstat_options_on_page', $settings );
225
-
226
- $tabs_html = '';
227
- foreach ( $settings as $a_tab_id => $a_tab_info ) {
228
- if ( !empty( $a_tab_info[ 'rows' ] ) || !empty( $a_tab_info[ 'include' ] ) ) {
229
- $tabs_html .= "<li class='nav-tab nav-tab".(($current_tab == $a_tab_id)?'-active':'-inactive')."'><a href='".wp_slimstat_admin::$config_url.$a_tab_id."'>{$a_tab_info[ 'title' ]}</a></li>";
230
- }
231
- }
232
-
233
- echo '<div class="wrap slimstat-config"><h2>'.__('Settings','wp-slimstat').'</h2><ul class="nav-tabs">'.$tabs_html.'</ul>';
234
- echo '<div class="notice slimstat-notice slimstat-tooltip-content" style="background-color:#ffa;border:0;padding:10px">' . __( '<strong>AdBlock browser extension detected</strong> - If you see this notice, it means that your browser is not loading our stylesheet and/or Javascript files correctly. This could be caused by an overzealous ad blocker feature enabled in your browser (AdBlock Plus and friends). <a href="https://slimstat.freshdesk.com/support/solutions/articles/12000000414-the-reports-are-not-being-rendered-correctly-or-buttons-do-not-work" target="_blank">Please make sure to add an exception</a> to your configuration and allow the browser to load these assets.', 'wp-slimstat' ) . '</div>';
235
-
236
- // The maintenance tab has its own separate file
237
- if ( !empty( $settings[ $current_tab ][ 'include' ] ) ) {
238
- include_once( $settings[ $current_tab ][ 'include' ] );
239
- }
240
- else if ( !empty( $settings[ $current_tab ][ 'rows' ] ) ) {
241
- wp_slimstat_admin::update_settings( $settings[ $current_tab ][ 'rows' ] );
242
- wp_slimstat_admin::display_settings( $settings[ $current_tab ][ 'rows' ], $current_tab );
243
- }
244
-
 
245
  echo '</div>';
1
+ <?php
2
+
3
+ // Avoid direct access to this piece of code
4
+ if ( !function_exists( 'add_action' ) ) {
5
+ exit(0);
6
+ }
7
+
8
+ if ( isset( $_POST[ 'options' ][ 'ignore_capabilities' ] ) ) {
9
+ // Make sure all the capabilities exist in the system
10
+ $capability_not_found = false;
11
+ foreach( wp_slimstat::string_to_array( $_POST[ 'options' ][ 'ignore_capabilities' ] ) as $a_capability ) {
12
+ if ( isset( $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) && !array_key_exists( $a_capability, $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) ) {
13
+ $capability_not_found = true;
14
+ break;
15
+ }
16
+ }
17
+
18
+ if ( !$capability_not_found ) {
19
+ wp_slimstat::$settings[ 'ignore_capabilities' ] = $_POST[ 'options' ][ 'ignore_capabilities' ];
20
+ }
21
+ else{
22
+ wp_slimstat_admin::$faulty_fields[] = __( 'Invalid capability. Please check <a href="http://codex.wordpress.org/Roles_and_Capabilities" target="_new">this page</a> for more information', 'wp-slimstat' );
23
+ }
24
+ }
25
+
26
+ if ( isset( $_POST[ 'options' ][ 'can_view' ] ) ) {
27
+ // Make sure all the users exist in the system
28
+ $post_data = trim( $_POST[ 'options' ][ 'can_view' ] );
29
+ $user_array = wp_slimstat::string_to_array( $_POST[ 'options' ][ 'can_view' ] );
30
+
31
+ if ( !empty( $post_data ) ) {
32
+ $sql_user_placeholders = implode( ', ', array_fill( 0, count( $user_array ), '%s' ) );
33
+ if ( $GLOBALS[ 'wpdb' ]->get_var( $GLOBALS[ 'wpdb' ]->prepare( "SELECT COUNT( * ) FROM {$GLOBALS[ 'wpdb' ]->users} WHERE user_login IN ( $sql_user_placeholders )", $user_array ) ) == count( $user_array ) ) {
34
+ wp_slimstat::$settings[ 'can_view' ] = $_POST[ 'options' ][ 'can_view' ];
35
+ }
36
+ else{
37
+ wp_slimstat_admin::$faulty_fields[] = __( 'Read access: username not found', 'wp-slimstat' );
38
+ }
39
+ }
40
+ else {
41
+ wp_slimstat::$settings[ 'can_view' ] = '';
42
+ }
43
+ }
44
+
45
+ if ( isset( $_POST[ 'options' ][ 'capability_can_view' ] ) ) {
46
+ if ( isset( $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) && array_key_exists( $_POST[ 'options' ][ 'capability_can_view' ], $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) ) {
47
+ wp_slimstat::$settings[ 'capability_can_view' ] = $_POST[ 'options' ][ 'capability_can_view' ];
48
+ }
49
+ else{
50
+ wp_slimstat_admin::$faulty_fields[] = __( 'Invalid capability. Please check <a href="http://codex.wordpress.org/Roles_and_Capabilities" target="_new">this page</a> for more information', 'wp-slimstat' );
51
+ }
52
+ }
53
+
54
+ if ( isset( $_POST[ 'options' ][ 'can_admin' ] ) ) {
55
+ // Make sure all the users exist in the system
56
+ $post_data = trim( $_POST[ 'options' ][ 'can_admin' ] );
57
+ $user_array = wp_slimstat::string_to_array( $_POST[ 'options' ][ 'can_admin' ] );
58
+
59
+ if ( is_array( $user_array ) && !empty( $post_data ) ) {
60
+ $sql_user_placeholders = implode( ', ', array_fill( 0, count( $user_array ), '%s' ) );
61
+ if ( $GLOBALS[ 'wpdb' ]->get_var( $GLOBALS[ 'wpdb' ]->prepare( "SELECT COUNT( * ) FROM {$GLOBALS[ 'wpdb' ]->users} WHERE user_login IN ( $sql_user_placeholders )", $user_array ) ) == count( $user_array ) ) {
62
+ wp_slimstat::$settings[ 'can_admin' ] = $_POST[ 'options' ][ 'can_admin' ];
63
+ }
64
+ else{
65
+ wp_slimstat_admin::$faulty_fields[] = __( 'Config access: username not found', 'wp-slimstat' );
66
+ }
67
+ }
68
+ }
69
+
70
+ if ( isset( $_POST[ 'options' ][ 'capability_can_admin' ] ) ) {
71
+ if ( isset( $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) && array_key_exists( $_POST[ 'options' ][ 'capability_can_admin' ], $GLOBALS[ 'wp_roles' ]->role_objects[ 'administrator' ]->capabilities ) ) {
72
+ wp_slimstat::$settings[ 'capability_can_admin' ] = $_POST[ 'options' ][ 'capability_can_admin' ];
73
+ }
74
+ else{
75
+ wp_slimstat_admin::$faulty_fields[] = __( 'Invalid capability. Please check <a href="http://codex.wordpress.org/Roles_and_Capabilities" target="_new">this page</a> for more information', 'wp-slimstat' );
76
+ }
77
+ }
78
+
79
+ $current_tab = empty( $_GET[ 'tab' ] ) ? 1 : intval( $_GET[ 'tab' ] );
80
+
81
+ // Define all the options
82
+ $settings = array(
83
+ 1 => array(
84
+ 'title' => __( 'Basic', 'wp-slimstat' ),
85
+ 'rows' => array(
86
+ 'general_tracking_header' => array( 'description' => __( 'Tracker', 'wp-slimstat' ), 'type' => 'section_header' ),
87
+ 'is_tracking' => array( 'description' => __( 'Enable Tracking', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Turn the tracker on or off, while keeping the reports accessible.', 'wp-slimstat' ) ),
88
+ 'javascript_mode' => array( 'description' => __( 'Tracking Mode', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __('Select <strong>Client</strong> if you are using a caching plugin (W3 Total Cache, WP SuperCache, HyperCache, etc). Slimstat will behave pretty much like Google Analytics, and visitors whose browser does not support Javascript will be ignored. A nice side effect is that <strong>most spammers, search engines and other crawlers</strong> will not be tracked.','wp-slimstat'), 'custom_label_on' => __('Client','wp-slimstat'), 'custom_label_off' => __('Server','wp-slimstat') ),
89
+ 'enable_javascript' => array( 'description' => __( 'Track Client Info', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "If this option is turned off, the tracker will not be collecting information such as screen resolution, outbound links, downloads, etc. This option is ignored if Tracking Mode is set to Client.", 'wp-slimstat' ) ),
90
+ 'track_admin_pages' => array( 'description' => __( 'Track Admin Pages', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Enable this option to track your users' activity within the admin.", 'wp-slimstat') ),
91
+
92
+ 'general_integration_header' => array( 'description' => __( 'WordPress Integration', 'wp-slimstat' ), 'type' => 'section_header' ),
93
+ 'add_dashboard_widgets' => array( 'description' => __( 'Dashboard Widgets', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Enable this option if you want to add reports to your WordPress Dashboard. Use the Customizer to choose which ones to display.', 'wp-slimstat' ) ),
94
+ 'use_separate_menu' => array( 'description' => __( 'Menu Position', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Choose between a standalone admin menu for Slimstat or a drop down in the admin bar (if visible).', 'wp-slimstat' ), 'custom_label_on' => __( 'Side', 'wp-slimstat' ), 'custom_label_off' => __( 'Bar', 'wp-slimstat' ) ),
95
+ 'posts_column_day_interval' => array( 'description' => __('Report Interval','wp-slimstat'), 'type' => 'integer', 'long_description' => __('Enter the time range, in days, that should be used to count the number of pageviews on Posts/Pages (see option here below) and for the Default Time Span option under the Reports tab.','wp-slimstat') ),
96
+ 'add_posts_column' => array( 'description' => __('Posts and Pages','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('Add a new column to the Edit Posts/Pages screens, with the number of hits per post within the timeframe specified here below.','wp-slimstat') ),
97
+
98
+ 'posts_column_pageviews' => array( 'description' => __( 'Report Type','wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Select what kind of information you would like to see displayed on your Posts admin screen. Hits counts all the pageviews regardless of the user, (unique) IPs counts only one hit per IP in the given time range.', 'wp-slimstat' ), 'custom_label_on' => __( 'Hits', 'wp-slimstat' ), 'custom_label_off' => __( 'IPs', 'wp-slimstat' ) ),
99
+ 'hide_addons' => array( 'description' => __( 'Hide Add-ons', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Enable this option to hide all your <strong>active</strong> premium add-ons from the list of plugins in WordPress. Please note that you will still receive updates for hidden add-ons.', 'wp-slimstat' ) ),
100
+
101
+ 'general_database_header' => array('description' => __('Database','wp-slimstat'), 'type' => 'section_header'),
102
+ '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 in the main table (neither archive it nor delete it) regardless of its age.", 'wp-slimstat' ).( (wp_slimstat::$settings[ 'auto_purge' ] > 0)?' '.__('Next clean-up on','wp-slimstat').' <strong>'.date_i18n(get_option('date_format').', '.get_option('time_format'), wp_next_scheduled( 'wp_update_plugins' ) ) . '</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::$settings['auto_purge'].' days'))):''), 'after_input_field' => __('days','wp-slimstat') ),
103
+ 'auto_purge_delete' => array( 'description' => __( 'Archive records', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'If DB space is not an issue, you can decide to archive older records in another table, instead of deleting them. This way performance is preserved, but you will still be able to access your data at a later time, if needed. Please note that the archive table (<code>wp_slim_stats_archive</code>) will be <strong>deleted</strong> along with all the other tables, when Slimstat is uninstalled. Make sure to backup your data before you proceed.', 'wp-slimstat' ) )
104
+ )
105
+ ),
106
+
107
+ 2 => array(
108
+ 'title' => __( 'Tracker', 'wp-slimstat' ),
109
+ 'rows' => array(
110
+ 'filters_outbound_header' => array( 'description' => __( 'Link Tracking', 'wp-slimstat' ), 'type' => 'section_header' ),
111
+ 'do_not_track_outbound_classes_rel_href' => array( 'description' => __( 'Do Not Track', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Slimstat will ignore links marked with one of these class names, <em>rel</em> attributes or whose <em>href</em> attribute contains one of these strings. Please keep in mind that the class <code>noslimstat</code> is also used to avoid tracking interactive links throughout the reports. If you remove it from this list, some features might not work as expected.", 'wp-slimstat' ) ),
112
+ 'extensions_to_track' => array( 'description' => __( 'Downloads', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( 'List all the file extensions that you want to be treated as Downloads. Please note that links pointing to external resources (i.e. PDFs on an external website) are considered Downloads and not Outbound Links (and tracked as such), if their extension matches one of the ones listed here below.', 'wp-slimstat' ) ),
113
+ 'track_same_domain_referers' => array( 'description' => __( 'Same-Domain Referrers', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'By default, when the domain of the referrer for a given page view is the same as the current site, that information is not tracked to save space in the database. However, if you are running a multisite network with subfolders, you might need to track same-domain referrers from one site to another, as they are technically "separate" websites.', 'wp-slimstat' ) ),
114
+
115
+ 'advanced_tracker_header' => array( 'description' => __( 'Advanced Options', 'wp-slimstat' ), 'type' => 'section_header' ),
116
+ 'geolocation_country' => array( 'description' => __( 'Geolocation Precision', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "When Slimstat determines your visitors' Country of origin, it uses a third-party data file <a href='https://dev.maxmind.com/geoip/geoip2/geolite2/' target='_blank'>provided by MaxMind</a>. They offer two precision levels: country and city. By default, Slimstat will install the smaller one (country), and you can decide to use the other one, if you don't mind its 60 Mb average size. After you change this option, please <strong>go to the Maintenance tab</strong> and reload (uninstall/install) the MaxMind GeoLite DB by clicking on the corresponding button.", 'wp-slimstat' ), 'custom_label_on' => __( 'Country', 'wp-slimstat' ), 'custom_label_off' => __( 'City', 'wp-slimstat' ) ),
117
+ 'session_duration' => array('description' => __( 'Session Duration', 'wp-slimstat' ), 'type' => 'integer', 'long_description' => __( 'How many seconds should a human session last? Google Analytics sets it to 1800 seconds.', 'wp-slimstat' ), 'after_input_field' => __( 'seconds', 'wp-slimstat' ) ),
118
+ 'extend_session' => array( 'description' => __( 'Extend Session', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Extend the duration of a session each time the user visits a new page.', 'wp-slimstat' ) ),
119
+ 'set_tracker_cookie' => array( 'description' => __( 'Set Cookie', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Disable this option if, for legal or security reasons, you do not want Slimstat to assign a <a href="https://en.wikipedia.org/wiki/HTTP_cookie" target="_blank">cookie</a> to your visitors. Please note that, by deactivating this feature, Slimstat will not keep track of returning visitors and sessions.', 'wp-slimstat' ) ),
120
+ 'enable_cdn' => array( 'description' => __( 'Enable CDN', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Use <a href='http://www.jsdelivr.com/' target='_blank'>JSDelivr</a>'s CDN, by serving our tracking code from their fast and reliable network (free service).", 'wp-slimstat' ) ),
121
+ 'ajax_relative_path' => array( 'description' => __( 'Relative Ajax', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'If you are experiencing issues related to the header field X-Requested-With not being allowed by Access-Control-Allow-Headers in preflight response (or similar), try enabling this option to make that <code>admin-ajax.php</code> URL relative.', 'wp-slimstat' ) ),
122
+
123
+ 'advanced_external_pages_header' => array('description' => __('External Pages','wp-slimstat'), 'type' => 'section_header'),
124
+ 'external_pages_script' => array('type' => 'static', 'skip_update' => 'yes', 'description' => __('Add the following code to all the non-WP pages you want to track, right before the closing BODY tag. Please make sure to change the protocol of all the URLs to HTTPS, if you external site is served over a secure channel.','wp-slimstat'), 'long_description' => '&lt;script type="text/javascript"&gt;
125
+ /* &lt;![CDATA[ */
126
+ var SlimStatParams = {
127
+ ajaxurl: "'.admin_url('admin-ajax.php').'",
128
+ ci: "YTo0OntzOjEyOiJjb250ZW50X3R5cGUiO3M6ODoiZXh0ZXJuYWwiO3M6ODoiY2F0ZWdvcnkiO3M6MDoiIjtzOjEwOiJjb250ZW50X2lkIjtpOjA7czo2OiJhdXRob3IiO3M6MTM6ImV4dGVybmFsLXBhZ2UiO30=.' . md5('YTo0OntzOjEyOiJjb250ZW50X3R5cGUiO3M6ODoiZXh0ZXJuYWwiO3M6ODoiY2F0ZWdvcnkiO3M6MDoiIjtzOjEwOiJjb250ZW50X2lkIjtpOjA7czo2OiJhdXRob3IiO3M6MTM6ImV4dGVybmFsLXBhZ2UiO30=' . wp_slimstat::$settings[ 'secret' ] ).'",
129
+ extensions_to_track: "'.wp_slimstat::$settings['extensions_to_track'].'"
130
+ };
131
+ /* ]]&gt; */
132
+ &lt;/script&gt;
133
+ &lt;script type="text/javascript" src="http://cdn.jsdelivr.net/wp/wp-slimstat/trunk/wp-slimstat.min.js"&gt;&lt;/script&gt;', 'use_tag_list' => false ),
134
+ 'external_domains' => array('description' => __('Allow Domains','wp-slimstat'), 'type' => 'textarea', 'long_description' => __("If you are getting an error saying that no 'Access-Control-Allow-Origin' header is present on the requested resource, when using the external tracking code here above, list the domains (complete with scheme) you would like to allow. For example: <code>http://my.domain.ext</code> (no trailing slash). Please see <a href='http://www.w3.org/TR/cors/#security' target='_blank'>this W3 resource</a> for more information on the security implications of allowing CORS requests.",'wp-slimstat'))
135
+ )
136
+ ),
137
+
138
+ 3 => array(
139
+ 'title' => __( 'Filters', 'wp-slimstat' ),
140
+ 'rows' => array(
141
+ 'filters_categories_header' => array( 'description' => __( 'Profiling', 'wp-slimstat' ), 'type' => 'section_header' ),
142
+ 'track_users' => array( 'description' => __( 'Track WP Users', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Enable this option to track logged in users.', 'wp-slimstat' ) ),
143
+ 'ignore_spammers' => array( 'description' => __( 'Ignore Spammers', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Enable this option if you don't want to track visits from users identified as spammers by third-party tools like Akismet. Pageviews generated by users whose comments are later marked as spam, will also be removed from the database.", 'wp-slimstat' ) ),
144
+ 'ignore_bots' => array( 'description' => __( 'Ignore Bots', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Turn on this feature if you want to have the accuracy level of server-side tracking, but not the inconvenience of getting your database clogged with pageviews generated by crawlers, spiders, search engine bots, etc. Please note that in Client mode, bots are ignored regardless of this setting.", 'wp-slimstat' ) ),
145
+ 'ignore_prefetch' => array( 'description' => __( 'Ignore Prefetch Requests', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Prevent Slimstat from tracking pageviews generated by Firefox's <a href='https://developer.mozilla.org/en/Link_prefetching_FAQ' target='_blank'>Link Prefetching functionality</a>.", 'wp-slimstat' ) ),
146
+ 'anonymize_ip' => array( 'description' => __( 'Enable Privacy Mode', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "Mask your visitors' IP addresses to comply with European Privacy Laws.", 'wp-slimstat' ) ),
147
+
148
+ 'filters_users_header' => array( 'description' => __( 'User Properties', 'wp-slimstat' ), 'type' => 'section_header' ),
149
+ 'ignore_users' => array( 'description' => __( 'Usernames', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "List all the usernames you don't want to track. Please be aware that spaces are <em>not</em> ignored and that usernames are case sensitive. Wildcards: <code>*</code> matches 'any string, including the empty string', <code>!</code> matches 'any character'. For example, <code>user*</code> will match user12 and userfoo, <code>u*100</code> will match user100 and uber100, <code>user!0</code> will match user10 and user90.", 'wp-slimstat' ) ),
150
+ 'ignore_ip' => array( 'description' => __( 'IP Addresses', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "List all the IP addresses you don't want to track. Each subnet <strong>must</strong> be defined using the <a href='https://www.iplocation.net/subnet-mask' target='_blank'>CIDR notation</a> (i.e. <em>192.168.0.0/24</em>). This filter applies both to the public IP and the originating IP, if available. Using the CIDR notation, you will use octets to determine the subnet, so for example 54.0.0.0/8 means that the first number is represented by 8 bits, hence 8 after the slash. Then the second number would be another 8 bits, so you would write 54.12.0.0/16 (16 = 8 + 8), and you could do the same for the third number, for example 54.12.34.0/24 (24 = 8 + 8 + 8).", 'wp-slimstat' ) ),
151
+ 'ignore_countries' => array( 'description' => __( 'Countries', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Country codes (i.e.: <code>en-us, it, es</code>) that you don't want to track.", 'wp-slimstat' ) ),
152
+ 'ignore_browsers' => array('description' => __( 'User Agents', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Browsers (user agents) you don't want to track. You can specify the browser's version adding a slash after the name (i.e. <em>Firefox/3.6</em>). Wildcards: <code>*</code> matches 'any string, including the empty string', <code>!</code> matches 'any character'. For example, <code>Chr*</code> will match Chrome and Chromium, <code>IE/!.0</code> will match IE/7.0 and IE/8.0. Strings are case-insensitive.", 'wp-slimstat' ) ),
153
+ 'ignore_platforms' => array( 'description' => __( 'Operating Systems', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Operating system codes you don't want to track. Please refer to <a href='https://slimstat.freshdesk.com/solution/articles/12000031504-what-are-the-operating-system-codes-used-by-slimstat-' target='_blank'>this page</a> in our knowledge base to learn more about what codes you can use. Usual rules for using wildcards apply (see fields here above).", 'wp-slimstat' ) ),
154
+ 'ignore_capabilities' => array( 'description' => __( 'Capabilities', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Users having at least one of the <a href='http://codex.wordpress.org/Roles_and_Capabilities' target='_new'>capabilities</a> listed here below will not be tracked. Capabilities are case-insensitive.", 'wp-slimstat' ), 'skip_update' => true ),
155
+
156
+ 'filters_pageview_header' => array( 'description' => __( 'Page Properties', 'wp-slimstat' ), 'type' => 'section_header' ),
157
+ 'ignore_resources' => array( 'description' => __( 'Permalinks', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "List all the URLs on your website that you don't want to track. Don't include the domain name: <em>/about, ?p=1</em>, etc. Wildcards: <code>*</code> matches 'any string, including the empty string', <code>!</code> matches 'any character'. For example, <code>/abou*</code> will match /about and /abound, <code>/abo*t</code> will match /aboundant and /about, <code>/abo!t</code> will match /about and /abort. Strings are case-insensitive.", 'wp-slimstat' ) ),
158
+ 'ignore_referers' => array( 'description' => __( 'Referring Sites', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Referring URLs that you don't want to track: <code>http://mysite.com*</code>, <code>*/ignore-me-please</code>, etc. Wildcards: <code>*</code> matches 'any string, including the empty string', <code>!</code> matches 'any character'. Strings are case-insensitive. Please include either a wildcard or the protocol you want to filter (http://, https://).", 'wp-slimstat' ) ),
159
+ 'ignore_content_types' => array( 'description' => __( 'Content Types', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Slimstat categorizes pageviews by the associated WordPress content type: post, page, attachment, tag, 404, taxonomy, author, archive, search, feed, login and others. You can use this field to avoid tracking pages whose content type matches the ones you set here below.", 'wp-slimstat' ) )
160
+ )
161
+ ),
162
+
163
+ 4 => array(
164
+ 'title' => __( 'Reports', 'wp-slimstat' ),
165
+ 'rows' => array(
166
+ 'reports_basic_header' => array( 'description' => __( 'Data Formats and Conversion', 'wp-slimstat' ), 'type' => 'section_header' ),
167
+ 'use_european_separators' => array( 'description' => __( 'Number Format', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Choose the number format you want to use for your reports.','wp-slimstat' ), 'custom_label_on' => '1.234,5', 'custom_label_off' => '1,234.5' ),
168
+ 'date_format' => array('description' => __('Date Format','wp-slimstat'), 'type' => 'text', 'long_description' => __("<a href='http://php.net/manual/en/function.date.php' target='_blank'>PHP Format</a> to use when displaying a pageview's date.", 'wp-slimstat')),
169
+ 'time_format' => array('description' => __('Time Format','wp-slimstat'), 'type' => 'text', 'long_description' => __("<a href='http://php.net/manual/en/function.date.php' target='_blank'>PHP Format</a> to use when displaying a pageview's time.", 'wp-slimstat')),
170
+ 'show_display_name' => array('description' => __('Use Display Name','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('By default, users are listed by their usernames. Use this option to visualize their display names instead.','wp-slimstat')),
171
+ 'convert_resource_urls_to_titles' => array('description' => __('Use Titles','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('Slimstat converts your permalinks into post, page and category titles. Disable this feature if you need to see the URL in your reports.', 'wp-slimstat')),
172
+ 'convert_ip_addresses' => array('description' => __('Convert IP Addresses','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('Display provider names instead of IP addresses.','wp-slimstat')),
173
+
174
+ 'reports_functionality_header' => array( 'description' => __( 'Functionality', 'wp-slimstat' ), 'type' => 'section_header' ),
175
+ 'async_load' => array( 'description' => __( 'Async Mode', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Activate this feature if your reports take a while to load. It breaks down the load on your server into multiple requests, thus avoiding memory issues and performance problems.', 'wp-slimstat' ) ),
176
+ 'use_current_month_timespan' => array( 'description' => __( 'Default Time Span', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Determine what is the default time period for calculating all the data in each report: current month or past given number of days. The number of days is defined under Basic > Report Interval. You can always use the time filter dropdown to customize this value even further.', 'wp-slimstat' ), 'custom_label_on' => 'Month', 'custom_label_off' => 'Days' ),
177
+ 'expand_details' => array('description' => __('Expand Details','wp-slimstat'), 'type' => 'toggle', 'long_description' => __("Expand each row's details by default, insted of on mousehover.",'wp-slimstat')),
178
+ 'rows_to_show' => array('description' => __('Rows to Display','wp-slimstat'), 'type' => 'integer', 'long_description' => __('Specify the number of items in each report.','wp-slimstat')),
179
+ 'limit_results' => array( 'description' => __( 'Max Results','wp-slimstat' ), 'type' => 'integer', 'long_description' => __( 'Decide how many records should be retrieved from the database in total. Depending on your server configuration, you may want to fine tune this value to avoid exceeding your PHP memory limit.', 'wp-slimstat' ) ),
180
+ 'ip_lookup_service' => array('description' => __( 'IP Lookup', 'wp-slimstat' ), 'type' => 'text', 'long_description' => __( 'Customize the Geolocation service to be used in the reports. Default: <code>http://www.infosniper.net/?ip_address=</code>', 'wp-slimstat' ) ),
181
+ 'mozcom_access_id' => array('description' => __( 'Mozscape Access ID', 'wp-slimstat' ), 'type' => 'text', 'long_description' => __( 'Get accurate rankings for your website through the free <a href="https://moz.com/community/join?redirect=/products/api/keys" target="_blank">Mozscape API</a> service. Sign up for a free community account to get started. Then enter your personal identification code here.', 'wp-slimstat' ) ),
182
+ 'mozcom_secret_key' => array('description' => __( 'Mozscape Secret Key', 'wp-slimstat' ), 'type' => 'text', 'long_description' => __( 'Do not share your secret key with anyone or they will be able to make API requests on your account!', 'wp-slimstat' ) ),
183
+
184
+ 'reports_right_now_header' => array( 'description' => __( 'Access Log and World Map', 'wp-slimstat' ), 'type' => 'section_header' ),
185
+ 'refresh_interval' => array( 'description' => __( 'Auto Refresh', 'wp-slimstat' ), 'type' => 'integer', 'long_description' => __( 'Enable the Live View, which refreshes the Access Log every X seconds. Enter <strong>0</strong> (number zero) to deactivate this feature.', 'wp-slimstat' ), 'after_input_field' => __( 'seconds', 'wp-slimstat' ) ),
186
+ 'number_results_raw_data' => array('description' => __( 'Rows to Display', 'wp-slimstat'), 'type' => 'integer', 'long_description' => __( 'Specify the number of items in the Access Log.', 'wp-slimstat' ) ),
187
+ 'max_dots_on_map' => array('description' => __( 'Map Data Points', 'wp-slimstat'), 'type' => 'integer', 'long_description' => __( 'Customize the maximum number of dots displayed on the world map. Please note that large numbers might increase the amount of time needed to render the map.', 'wp-slimstat' ) ),
188
+
189
+ 'reports_miscellaneous_header' => array('description' => __('Miscellaneous','wp-slimstat'), 'type' => 'section_header'),
190
+ 'custom_css' => array( 'description' => __( 'Custom CSS', 'wp-slimstat' ), 'type' => 'textarea', 'rows' => 8, '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' ), 'use_tag_list' => false ),
191
+ 'chart_colors' => array( 'description' => __( 'Chart Colors', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "Customize the look and feel of your charts by assigning personalized colors to each metric. List 4 hex colors, strictly in the following order: metric 1 previous, metric 2 previous, metric 1 current, metric 2 current. For example: <code>#ccc, #999, #bbcc44, #21759b</code>.", 'wp-slimstat' ) ),
192
+ 'comparison_chart' => array( 'description' => __( 'Comparison Chart', 'wp-slimstat'), 'type' => 'toggle', 'long_description' => __( "Disable this option if you find the four line charts confusing, and prefer seeing only the selected time range.", 'wp-slimstat' ) ),
193
+ 'show_complete_user_agent_tooltip' => array('description' => __('Show User Agent','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('Choose if you want to see the browser name or a complete user agent string when hovering over each browser icon.','wp-slimstat')),
194
+ 'enable_sov' => array('description' => __('Enable SOV','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('In linguistic typology, a subject-object-verb (SOV) language is one in which the subject, object, and verb of a sentence appear in that order, like in Japanese.','wp-slimstat'))
195
+ )
196
+ ),
197
+
198
+ 5 => array(
199
+ 'title' => __( 'Access Control', 'wp-slimstat' ),
200
+ 'rows' => array(
201
+ 'permissions_reports_header' => array('description' => __('Reports','wp-slimstat'), 'type' => 'section_header'),
202
+ 'restrict_authors_view' => array('description' => __('Restrict Authors','wp-slimstat'), 'type' => 'toggle', 'long_description' => __('Enable this option if you want your authors to only see stats related to their own content.','wp-slimstat')),
203
+ 'capability_can_view' => array('description' => __('Capability','wp-slimstat'), 'type' => 'text', 'long_description' => __("Specify the minimum <a href='http://codex.wordpress.org/Roles_and_Capabilities' target='_new'>capability</a> needed to access the reports (default: <code>activate_plugins</code>). If this field is empty, <strong>all your users</strong> (including subscribers) will have access to the reports, unless a 'Read access' whitelist has been specified here below. In this case, the list has precedence over the capability.",'wp-slimstat')),
204
+ 'can_view' => array('description' => __('Whitelist','wp-slimstat'), 'type' => 'textarea', 'long_description' => __("List all the users who should have access to the reports. Administrators are implicitly allowed, so you don't need to list them in here. Usernames are case sensitive.",'wp-slimstat'), 'skip_update' => true),
205
+
206
+ 'permissions_config_header' => array('description' => __('Settings','wp-slimstat'), 'type' => 'section_header'),
207
+ 'capability_can_admin' => array('description' => __('Capability','wp-slimstat'), 'type' => 'text', 'long_description' => __("Specify the minimum <a href='http://codex.wordpress.org/Roles_and_Capabilities' target='_new'>capability</a> required to configure Slimstat (default: <code>activate_plugins</code>). The whitelist here below can be used to override this option for specific users.",'wp-slimstat')),
208
+ 'can_admin' => array('description' => __('Whitelist','wp-slimstat'), 'type' => 'textarea', 'long_description' => __("List all the users who can edit these options. Please be advised that admins <strong>are not</strong> implicitly allowed, so do not forget to include yourself! Usernames are case sensitive.",'wp-slimstat'), 'skip_update' => true),
209
+
210
+ 'rest_api_header' => array( 'description' => __( 'Rest API', 'wp-slimstat' ), 'type' => 'section_header' ),
211
+ 'rest_api_tokens' => array( 'description' => __( 'Tokens', 'wp-slimstat' ), 'type' => 'textarea', 'long_description' => __( "In order to send requests to <a href='https://slimstat.freshdesk.com/support/solutions/articles/12000033661-slimstat-rest-api' target='_blank'>the Slimstat REST API</a>, you will need to pass a valid token to the endpoint (param ?token=XXX). Using the field here below, you can define as many tokens as you like, to distribute them to your API users. Please note: treat these tokens as passwords, as they will grant read access to your reports to anyone who knows them. Use a service like <a href='https://randomkeygen.com/#ci_key' target='_blank'>RandomKeyGen.com</a> to generate unique secure tokens.", 'wp-slimstat' ) )
212
+ )
213
+ ),
214
+
215
+ 6 => array(
216
+ 'title' => __( 'Maintenance', 'wp-slimstat' ),
217
+ 'include' => dirname(__FILE__).'/maintenance.php'
218
+ ),
219
+
220
+ 7 => array(
221
+ 'title' => __( 'Add-ons', 'wp-slimstat' )
222
+ )
223
+ );
224
+
225
+ $settings = apply_filters( 'slimstat_options_on_page', $settings );
226
+
227
+ $tabs_html = '';
228
+ foreach ( $settings as $a_tab_id => $a_tab_info ) {
229
+ if ( !empty( $a_tab_info[ 'rows' ] ) || !empty( $a_tab_info[ 'include' ] ) ) {
230
+ $tabs_html .= "<li class='nav-tab nav-tab".(($current_tab == $a_tab_id)?'-active':'-inactive')."'><a href='".wp_slimstat_admin::$config_url.$a_tab_id."'>{$a_tab_info[ 'title' ]}</a></li>";
231
+ }
232
+ }
233
+
234
+ echo '<div class="wrap slimstat-config"><h2>'.__('Settings','wp-slimstat').'</h2><ul class="nav-tabs">'.$tabs_html.'</ul>';
235
+ echo '<div class="notice slimstat-notice slimstat-tooltip-content" style="background-color:#ffa;border:0;padding:10px">' . __( '<strong>AdBlock browser extension detected</strong> - If you see this notice, it means that your browser is not loading our stylesheet and/or Javascript files correctly. This could be caused by an overzealous ad blocker feature enabled in your browser (AdBlock Plus and friends). <a href="https://slimstat.freshdesk.com/support/solutions/articles/12000000414-the-reports-are-not-being-rendered-correctly-or-buttons-do-not-work" target="_blank">Please make sure to add an exception</a> to your configuration and allow the browser to load these assets.', 'wp-slimstat' ) . '</div>';
236
+
237
+ // The maintenance tab has its own separate file
238
+ if ( !empty( $settings[ $current_tab ][ 'include' ] ) ) {
239
+ include_once( $settings[ $current_tab ][ 'include' ] );
240
+ }
241
+ else if ( !empty( $settings[ $current_tab ][ 'rows' ] ) ) {
242
+ wp_slimstat_admin::update_settings( $settings[ $current_tab ][ 'rows' ] );
243
+ wp_slimstat_admin::display_settings( $settings[ $current_tab ][ 'rows' ], $current_tab );
244
+ }
245
+
246
  echo '</div>';
admin/css/slimstat.css CHANGED
@@ -1,764 +1,765 @@
1
- /* Links */
2
- .wrap.slimstat a, [id^=slim_] a{
3
- outline: none;
4
- text-decoration: none;
5
- }
6
- [id^=slim_] a.slimstat-delete-entry:before{
7
- color: #e22;
8
- }
9
- .slimstat-float-right{
10
- float: right;
11
- }
12
-
13
- /* Admin Notice */
14
- .slimstat-notice{
15
- overflow: hidden;
16
- }
17
- .slimstat-notice span{
18
- width: 97%;
19
- }
20
- .slimstat-notice .fixed-height {
21
- height: 120px;
22
- margin-bottom: 20px;
23
- overflow: auto;
24
- padding: 0 20px 0 0;
25
- }
26
-
27
- /* Filters */
28
- #slimstat-filters-post{
29
- display: none;
30
- }
31
- #slimstat-filters-form{
32
- background-color: #e0e0e0;
33
- margin-top: 10px;
34
- padding: 5px;
35
- position: relative;
36
- }
37
- #slimstat-filters input.text, #slimstat-filters select{
38
- vertical-align: initial;
39
- width: 15%;
40
- }
41
- #slimstat-filters-form label {
42
- left:-9000px;
43
- position: absolute;
44
- }
45
- #slimstat-date-filters{
46
- padding: 4px 10px 6px 20px;
47
- position: absolute;
48
- right: 5px;
49
- top: 5px;
50
- }
51
- #slimstat-date-filters>a{
52
- color: #fff;
53
- white-space: nowrap;
54
- }
55
- #slimstat-date-filters>a::after{
56
- content: "\f140";
57
- font: normal 20px/1 'dashicons';
58
- vertical-align: bottom;
59
- }
60
- #slimstat-date-filters>a.open::after{
61
- content: "\f142";
62
- }
63
- #slimstat-date-filters .dropdown {
64
- background-color: #fff;
65
- box-shadow: 0 1px 8px rgba(0, 0, 0, 0.2);
66
- color: #222;
67
- display: none;
68
- padding: 5px 0 5px 5px;
69
- position: absolute;
70
- right: -4px;
71
- top: 35px;
72
- width: 450px;
73
- z-index: 120;
74
- }
75
- #slimstat-date-filters .dropdown #slimstat-quick-filters a {
76
- background-color: #eee;
77
- display: block;
78
- float: left;
79
- margin: 0 5px 5px 0;
80
- padding: 5px;
81
- width: 135px;
82
- }
83
- #slimstat-date-filters .dropdown strong{
84
- clear: both;
85
- display: block;
86
- font-weight: normal;
87
- padding: 10px 5px 5px 0;
88
- text-transform: uppercase;
89
- }
90
- #slimstat-date-filters .dropdown select, #slimstat-date-filters .dropdown input{
91
- margin-bottom: 5px;
92
- width: 80px;
93
- }
94
- #slimstat-date-filters .dropdown select.short, #slimstat-date-filters .dropdown input.short{
95
- width: 50px;
96
- }
97
- #slimstat-date-filters .dropdown .ui-datepicker-trigger{
98
- float: left;
99
- margin: 2px 5px 0 0;
100
- }
101
- #slimstat-current-filters{
102
- background-color: #ccc;
103
- border-top: 2px solid #eee;
104
- color: #222;
105
- font-size: 1.1em;
106
- overflow: hidden;
107
- padding: 5px;
108
- position: relative
109
- }
110
- #slimstat-current-filters .slimstat-remove-filter{
111
- color: #fff;
112
- }
113
- #slimstat-current-filters .slimstat-filter-list{
114
- float: left;
115
- margin: 0;
116
- padding: 4px 0 5px 5px;;
117
- }
118
- #slimstat-current-filters .slimstat-filter-list li{
119
- display: inline;
120
- margin: 0 20px 0 0;
121
- vertical-align: middle;
122
- }
123
- .slimstat-filter-action-button{
124
- float: right;
125
- margin-left: 10px !important;
126
- }
127
-
128
- /* Reports: Container */
129
- .wrap.slimstat .meta-box-sortables {
130
- margin-top: 20px;
131
- }
132
-
133
- /* Single Report */
134
- .wrap.slimstat .postbox, .wrap.slimstat .sortable-placeholder {
135
- box-sizing: border-box;
136
- float: left;
137
- margin-bottom: 7px;
138
- margin-right: 0.5%;
139
- min-width: 24.3%;
140
- overflow: hidden;
141
- width: 24.3%;
142
- }
143
- .wrap.slimstat .postbox.large {
144
- width: 49.1%;
145
- }
146
- .wrap.slimstat .postbox.extralarge {
147
- width: 73.9%;
148
- }
149
- .wrap.slimstat .postbox.full-width {
150
- width: 98.7%;
151
- }
152
-
153
- .sortable-placeholder{
154
- background-color: #ccc;
155
- border: 1px dashed #bbb;
156
- margin-bottom: 9px
157
- }
158
-
159
- /* Single Report: Header and Header Buttons */
160
- .wrap.slimstat .postbox h3{
161
- border-bottom: 1px solid #ddd;
162
- font-size: 1.2em;
163
- margin: 0;
164
- padding: 10px;
165
- }
166
- .wrap.slimstat .postbox h3:hover:after {
167
- color: #bbb;
168
- content: attr(data-report-id);
169
- margin-left: 10px;
170
- }
171
- .wrap.slimstat .postbox.tall h3{
172
- cursor:auto;
173
- }
174
- .slimstat-header-buttons{
175
- float: right;
176
- padding: 10px 10px 0;
177
- }
178
- .slimstat-tooltip-trigger.corner{
179
- background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAABkSURBVHjaYpw1a9YUBgaGbCD+AsR/GRCACYg/sACJXCBWBGIvBkzAC1L1H4i9gXgRFgUfmZA48UC8EF0FExo/AYgXIPH/MWExNhGI50PZnNgUgEASEG8FYg5cCkDAB4gnAAQYAGBCD4bcPf1fAAAAAElFTkSuQmCC) 0 0 no-repeat scroll;
180
- cursor: help;
181
- display: block;
182
- height: 7px;
183
- position: absolute;
184
- top: 0;
185
- width: 7px;
186
- }
187
- .slimstat-tooltip-content{
188
- display: none;
189
- }
190
- .wrap.slimstat h3 .slimstat-tooltip-trigger.corner{
191
- right: 0;
192
- }
193
- .slimstat .no-refresh .refresh{
194
- display: none;
195
- }
196
-
197
- /* Single Report: Header and Header Buttons - Pagination */
198
- [id^=slim_] p.pagination{
199
- background-color: #e0e0e0;
200
- border-bottom: 1px solid #cde;
201
- border-top: 1px solid #cde;
202
- color: #444;
203
- line-height: 1em;
204
- min-height: 5px;
205
- }
206
- [id^=slim_] p.pagination a{
207
- color: #444;
208
- float: right;
209
- }
210
- [id^=slim_] p.pagination a:hover{
211
- text-shadow: 0 0 2px #000;
212
- }
213
-
214
- /* Single Report: Inside */
215
- [id^=slim_] .inside{
216
- height: 281px;
217
- margin: 0 !important;
218
- overflow: auto;
219
- padding: 0 !important;
220
- }
221
- .wrap.slimstat .postbox.tall .inside{
222
- height: 650px;
223
- }
224
- [id^=slim_] p {
225
- border-bottom: 1px solid #ddd;
226
- line-height: 1.5em;
227
- margin: 0;
228
- min-height: 20px;
229
- overflow: auto;
230
- padding: 7px 10px;
231
- position: relative;
232
- word-wrap: break-word;
233
- }
234
- [id^=slim_] p:last-child{
235
- border-bottom:0;
236
- }
237
- [id^=slim_] p .slimstat-tooltip-trigger.corner{
238
- -moz-transform: scaleX(-1);
239
- -o-transform: scaleX(-1);
240
- -webkit-transform: scaleX(-1);
241
- transform: scaleX(-1);
242
- filter: FlipH;
243
- -ms-filter: "FlipH";
244
- left: 0;
245
- }
246
- [id^=slim_] p.header{
247
- background-color:#eee;
248
- border-color:#a6a6a6;
249
- }
250
- [id^=slim_] p span{
251
- float: right;
252
- }
253
- [id^=slim_] p span.pageview-screenres{
254
- margin-right: 10px;
255
- }
256
- [id^=slim_] a:hover{
257
- text-shadow: 0 0 2px #bbb;
258
- }
259
- [id^=slim_] .slimstat-tooltip-content.expanded{
260
- display: block;
261
- font-weight: normal;
262
- }
263
- [id^=slim_] .inline-icon{
264
- background-color: transparent;
265
- background-position: 0 0;
266
- background-repeat: no-repeat;
267
- display: inline-block;
268
- height: 18px;
269
- line-height: 18px;
270
- margin-right: 5px;
271
- vertical-align: middle;
272
- width: 16px;
273
- }
274
- [id^=slim_] .spaced{
275
- margin-left:15px;
276
- }
277
- #dashboard-widgets-wrap .whois{
278
- display: none;
279
- }
280
-
281
- /* Single Report: Table */
282
- [id^=slim_] .widefat th{
283
- white-space: nowrap;
284
- }
285
- [id^=slim_] .widefat td{
286
- border-bottom: 1px solid #e1e1e1;
287
- }
288
-
289
- /* Single Report: Debug Message */
290
- [id^=slim_] .debug{
291
- background-color: #000;
292
- color: #fff;
293
- display: block;
294
- font-family: monospace;
295
- opacity: .8;
296
- padding: 20px;
297
- position: relative;
298
- }
299
-
300
- /* Single Report: Color Coded Entries */
301
- .little-color-box{
302
- background-color: #eee;
303
- border: 1px solid #aaa;
304
- display: block;
305
- float: left;
306
- height: 15px;
307
- margin-right: 10px;
308
- width: 15px;
309
- }
310
- [id^=slim_] .header.is-search-engine, .little-color-box.is-search-engine{
311
- background-color:#c1e751;
312
- color:#444;
313
- }
314
- [id^=slim_] .header.is-direct, .little-color-box.is-direct{
315
- background-color:#d0e0eb;
316
- color:#111;
317
- }
318
- [id^=slim_] .header.is-known-user, .little-color-box.is-known-user{
319
- background-color:#F1CF90;
320
- }
321
- [id^=slim_] .header.is-known-visitor, .little-color-box.is-known-visitor{
322
- background-color:#EFFD8C;
323
- }
324
- [id^=slim_] .header.is-spam, .little-color-box.is-spam{
325
- background-color:#AAB3AB;
326
- color:#222;
327
- }
328
- [id^=slim_] p.loading{
329
- font-size: 30px;
330
- line-height: 4em;
331
- margin-top: 60px;
332
- text-align: center;
333
- }
334
- [id^=slim_] p.nodata{
335
- border: 0;
336
- color: #999;
337
- padding-top: 95px;
338
- text-align: center;
339
- }
340
-
341
- /* Single Report: Chart */
342
- .chart-placeholder{
343
- height: 280px;
344
- }
345
-
346
- /* Single Report: Modal Window */
347
- .ui-widget-overlay{
348
- background-color: #222;
349
- filter: alpha(opacity=60);
350
- height: 100%;
351
- opacity: .6;
352
- position: fixed;
353
- top: 0;
354
- width: 100%;
355
- z-index: 100 !important;
356
- }
357
- .ui-dialog.slimstat{
358
- border: 0;
359
- border-radius: 0;
360
- padding: 0;
361
- width: 90% !important;
362
- z-index: 100500 !important;
363
- }
364
- .ui-dialog.slimstat .ui-dialog-titlebar{
365
- background: #4b8df8;
366
- border: 0;
367
- border-radius: 0;
368
- color: #fff;
369
- font-family: 'Open Sans', sans-serif;
370
- font-size: 1.3em;
371
- font-weight: normal;
372
- height: 22px;
373
- line-height: 1.3em;
374
- margin: 0;
375
- padding: 5px 10px;
376
- }
377
- .ui-dialog.slimstat .ui-dialog-titlebar-close{
378
- background-color: transparent;
379
- border: 0;
380
- color: #fff;
381
- float: right;
382
- line-height: 1.3em;
383
- padding: 0;
384
- }
385
- .ui-dialog.slimstat .ui-dialog-titlebar-close:before{
386
- content:'\e803';
387
- font-family: slimstat;
388
- }
389
- .ui-dialog .ui-dialog-content{
390
- clear: both;
391
- }
392
- #slimstat-modal-dialog{
393
- background-color: #fff;
394
- display: none;
395
- max-height: 650px !important;
396
- overflow: auto;
397
- padding: 0;
398
- width: auto;
399
- }
400
- #slimstat-modal-dialog p{
401
- margin: 0;
402
- padding: 10px;
403
- }
404
-
405
- /* Dashboard Widgets */
406
- #dashboard-widgets [id*=slim_p].postbox .inside {
407
- height: 281px;
408
- }
409
- #dashboard-widgets [id*=slim_p][id*=_01].postbox .inside {
410
- height: 290px;
411
- }
412
- #dashboard-widgets #slim_p7_02.postbox .inside {
413
- height: 320px;
414
- }
415
- .closed .slimScrollDiv{
416
- height: 0 !important;
417
- }
418
-
419
- /* Settings */
420
- #slimstat-message{
421
- background-color: #555;
422
- clear: both;
423
- color: #fff;
424
- overflow: hidden;
425
- padding: 0 10px;
426
- }
427
- #slimstat-message p:before{
428
- content: "\f339";
429
- font-family: 'dashicons';
430
- margin-right: 10px;
431
- vertical-align: bottom;
432
- }
433
- #slimstat-message.wp-ui-notification p:before{
434
- content: "\f160";
435
- }
436
- #slimstat-message p{
437
- line-height: 1em;
438
- }
439
- #slimstat-message a{
440
- color: #fc0;
441
- }
442
- .nav-tabs{
443
- margin: 20px 1px 0;
444
- }
445
- .nav-tab{
446
- border-width: 1px 1px 0 !important;
447
- font-size: 14px;
448
- margin: 0 5px 0 0;
449
- }
450
- .nav-tab.nav-tab-active{
451
- background-color: #fff !important;
452
- }
453
- .nav-tab a{
454
- color: #444;
455
- display: block;
456
- text-decoration: none;
457
- }
458
- .form-table{
459
- border: 1px solid #ccc !important;
460
- margin-top: 0 !important;
461
- }
462
- .form-table th{
463
- font-weight: 400 !important;
464
- padding: 15px 10px !important;
465
- }
466
- .slimstat-options-section-header {
467
- background-color: #e4e4e4;
468
- font-size: 1.4em !important;
469
- margin: 0;
470
- padding: 5px 10px !important;
471
- }
472
- .form-table h3{
473
- margin-top: 0;
474
- }
475
- .form-table td span.block-element{
476
- padding: 0 30px 0 0;
477
- }
478
- .form-table .description{
479
- color: #999;
480
- display: block;
481
- font-style: normal;
482
- margin-top: 5px;
483
- }
484
- .form-table #slimstat-filter-name, .form-table #slimstat-filter-operator, .form-table #slimstat-filter-value{
485
- width: 20%;
486
- }
487
- [id*=form-slimstat-options] input[type=number].small-text{
488
- width: 85px;
489
- }
490
- [class*=bootstrap-switch-id-addon_network_settings] {
491
- float: right;
492
- }
493
-
494
- /* Add-ons Page */
495
- .wp-list-table.slimstat-addons{
496
- margin-bottom: 20px;
497
- }
498
- .wp-list-table.slimstat-addons tbody th{
499
- border-left: 5px solid #ccc;
500
- }
501
- .wp-list-table.slimstat-addons th,.wp-list-table.slimstat-addons td{
502
- border-bottom: 1px solid #ccc;
503
- }
504
- .wp-list-table.slimstat-addons .active th{
505
- border-color: #10a062;
506
- border-style: solid;
507
- border-width: 0 0 0 5px;
508
- }
509
- .wp-list-table.slimstat-addons .active td{
510
- border: 0;
511
- }
512
-
513
- /* Edit Posts Page */
514
- .column-wp-slimstat{
515
- text-align: center !important;
516
- width: 3em;
517
- }
518
- .slimstat-icon:before{
519
- content: '\f239';
520
- font-family: dashicons;
521
- }
522
-
523
- /* RTL Support */
524
- .rtl #slimstat-current-filters .slimstat-filter-list{
525
- padding: 0 0 0 75px;
526
- }
527
- .rtl #slimstat-current-filters .slimstat-filter-list li{
528
- margin-left: 20px;
529
- margin-right: 0;
530
- }
531
- .rtl #slimstat-remove-all-filters{
532
- left: 5px;
533
- right: inherit;
534
- }
535
- .rtl #slimstat-date-filters{
536
- left: 5px;
537
- right: inherit;
538
- }
539
- .rtl #slimstat-date-filters span{
540
- left: 0;
541
- right: inherit;
542
- }
543
- .rtl .wrap.slimstat .postbox{
544
- float: right;
545
- }
546
- .rtl .slimScrollBar{
547
- left: 2px !important;
548
- right: inherit !important;
549
- }
550
- .rtl .slimstat-header-buttons{
551
- float: left;
552
- }
553
- .rtl [id^=slim_] p.pagination a{
554
- float: left;
555
- }
556
- .rtl [id^=slim_] p span{
557
- float: left;
558
- }
559
- .rtl .qtip-content{
560
- text-align: right;
561
- }
562
- .rtl .form-table td span.block-element{
563
- padding: 0 0 0 30px;
564
- }
565
-
566
- /* Customizer */
567
- .slimstat-layout .postbox-container {
568
- border: 1px solid #ccc;
569
- float: none;
570
- margin-bottom: 20px;
571
- overflow: hidden;
572
- }
573
- .slimstat-layout .meta-box-sortables {
574
- overflow: hidden;
575
- padding: 10px;
576
- }
577
- .slimstat-layout .postbox-container span.title {
578
- background-color: #c0c0c0;
579
- color: #fff;
580
- font-size: 16px;
581
- margin-bottom: 0;
582
- padding: 10px;
583
- }
584
- .slimstat-layout .postbox, .slimstat-layout .sortable-placeholder {
585
- float: left;
586
- margin: 0 10px 10px 0;
587
- min-width: 285px;
588
- }
589
- .slimstat-layout .postbox.invisible h3 {
590
- color: #ccc;
591
- }
592
- .slimstat-layout h3 {
593
- border: 0;
594
- font-weight: 300;
595
- font-size: 1.1em;
596
- margin: 0;
597
- padding: 5px 10px;
598
- }
599
- .slimstat-layout #postbox-container-slimview1 {
600
- display: none;
601
- }
602
-
603
- /* Responsive */
604
- @media screen and ( max-width: 1560px ) {
605
- .wrap.slimstat .postbox {
606
- width: 32.8%;
607
- }
608
- .wrap.slimstat .postbox.large {
609
- width: 66.1%;
610
- }
611
- .wrap.slimstat .postbox.extralarge, .wrap.slimstat .postbox.full-width {
612
- width: 99.4%;
613
- }
614
- .wrap.slimstat .postbox h3 {
615
- font-size: 1em;
616
- line-height: 1.5em;
617
- }
618
- .wrap.slimstat .postbox p {
619
- font-size: 1em;
620
- line-height: 1.5em;
621
- }
622
- }
623
-
624
- @media screen and ( max-width: 1080px ) {
625
- .wrap.slimstat .postbox{
626
- width: 49.125%;
627
- }
628
- .wrap.slimstat .postbox.large, .wrap.slimstat .postbox.extralarge, .wrap.slimstat .postbox.full-width {
629
- width: 99%;
630
- }
631
- }
632
- @media screen and (max-width: 800px){
633
- .slimstat-notice span {
634
- width: 90%;
635
- }
636
- #slimstat-filters input.text, #slimstat-filters select {
637
- margin: 0 .5% 0 0;
638
- width: 26%;
639
- }
640
- .wp-core-ui .button-secondary {
641
- height: 35px;
642
- }
643
- #slimstat-date-filters {
644
- margin-top: 5px;
645
- position: relative;
646
- right: inherit;
647
- top: inherit;
648
- }
649
- .wrap.slimstat .postbox, .wrap.slimstat .postbox.large, .wrap.slimstat .postbox.extralarge, .wrap.slimstat .postbox.full-width {
650
- margin: 0 0 10px;
651
- width: 99.5%;
652
- }
653
- .wrap.slimstat .postbox h3 {
654
- font-size: 1.4em;
655
- line-height: 1em;
656
- }
657
- .wrap.slimstat .postbox p {
658
- font-size: 1.1em;
659
- line-height: 1.4em;
660
- }
661
-
662
- /* Settings */
663
- .nav-tab {
664
- display: block;
665
- margin: 0;
666
- }
667
- .form-table th {
668
- font-size: 1.4em;
669
- }
670
- .form-table th label {
671
- font-size: 1em;
672
- }
673
- .form-table td {
674
- padding: 10px;
675
- }
676
- .form-table .button-primary,.form-table .button-secondary {
677
- height: 30px;
678
- margin-bottom: 5px;
679
- text-align: center;
680
- width: 100%;
681
- }
682
- .form-table #slimstat-filter-name, .form-table #slimstat-filter-operator, .form-table #slimstat-filter-value {
683
- width: 100%;
684
- }
685
-
686
- /* Add-on: WP SlimStat - User Overview */
687
- [id^='slim_'] .users .column-name {
688
- display: none;
689
- }
690
- }
691
-
692
- /* Fontello - Custom Icon Fonts for SlimStat | Uses Fontelico (C) 2012 by Fontello project, Elusive (C) 2013 by Aristeides Stathopoulos, Font Awesome (C) 2012 by Dave Gandy | http://www.fontello.com */
693
- @font-face {
694
- font-family: 'slimstat';
695
- src: url('slimstat.eot?58272494');
696
- src: url('slimstat.eot?58272494#iefix') format('embedded-opentype'),
697
- url('slimstat.svg?58272494#fontello') format('svg');
698
- font-weight: normal;
699
- font-style: normal;
700
- }
701
- @font-face {
702
- font-family: 'slimstat';
703
- src: url('data:application/octet-stream;base64,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') format('woff'),
704
- url('data:application/octet-stream;base64,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') format('truetype');
705
- }
706
- [class^="slimstat-font-"]:before, [class*=" slimstat-font-"]:before {
707
- color: #aaa;
708
- font-family: "slimstat";
709
- font-style: normal;
710
- font-weight: normal;
711
- speak: none;
712
-
713
- text-decoration: inherit;
714
- width: 1em;
715
- margin: 0 .2em;
716
- text-align: center;
717
-
718
- /* For safety - reset parent styles, that can break glyph codes*/
719
- font-variant: normal;
720
- text-transform: none;
721
-
722
- /* fix buttons height, for twitter bootstrap */
723
- line-height: 1em;
724
- }
725
- .slimstat-font-trash:before { content: '\e800'; } /* '' */
726
- .slimstat-font-clock:before { content: '\e801'; } /* '' */
727
- .slimstat-font-search:before { content: '\e802'; } /* '' */
728
- .slimstat-font-cancel:before { content: '\e803'; } /* '' */
729
- .slimstat-font-download:before { content: '\e804'; } /* '' */
730
- .slimstat-font-upload:before { content: '\e805'; } /* '' */
731
- .slimstat-font-download-cloud:before { content: '\e806'; } /* '' */
732
- .slimstat-font-upload-cloud:before { content: '\e807'; } /* '' */
733
- .slimstat-font-location-1:before { content: '\e808'; } /* '' */
734
- .slimstat-font-arrows-cw-1:before { content: '\e809'; } /* '' */
735
- .slimstat-font-login:before { content: '\e80a'; } /* '' */
736
- .slimstat-font-logout:before { content: '\e80b'; } /* '' */
737
- .slimstat-font-angle-left:before { content: '\e80c'; } /* '' */
738
- .slimstat-font-angle-right:before { content: '\e80d'; } /* '' */
739
- .slimstat-font-angle-double-left:before { content: '\e80e'; } /* '' */
740
- .slimstat-font-angle-double-right:before { content: '\e80f'; } /* '' */
741
- .slimstat-font-arrows-cw:before { content: '\e810'; } /* '' */
742
- .slimstat-font-doc:before { content: '\e811'; } /* '' */
743
- .slimstat-font-spin4:before { content: '\e812'; } /* '' */
744
- .slimstat-font-edit:before { content: '\e813'; } /* '' */
745
- .slimstat-font-gauge:before { content: '\e814'; } /* '' */
746
- .slimstat-font-stopwatch:before { content: '\e815'; } /* '' */
747
- .slimstat-font-docs:before { content: '\e816'; } /* '' */
748
- .slimstat-font-user-plus:before { content: '\e817'; } /* '' */
749
- .slimstat-font-user-times:before { content: '\e818'; } /* '' */
750
- .slimstat-font-minus-circled:before { content: '\e819'; } /* '' */
751
-
752
- .animate-spin{-moz-animation:spin 3s infinite linear;-o-animation:spin 3s infinite linear;-webkit-animation:spin 3s infinite linear;animation:spin 3s infinite linear;display:inline-block;line-height:1em;}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-o-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-ms-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}
753
-
754
- /* qTip2 v3.0.3 basic | http://qtip2.com */
755
- .qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;direction:ltr;box-shadow:none;padding:0}.qtip-content,.qtip-titlebar{position:relative;overflow:hidden}.qtip-content{padding:5px 9px;text-align:left;word-wrap:break-word}.qtip-titlebar{padding:5px 35px 5px 10px;border-width:0 0 1px;font-weight:700}.qtip-titlebar+.qtip-content{border-top-width:0!important}.qtip-close{position:absolute;right:-9px;top:-9px;z-index:11;cursor:pointer;outline:0;border:1px solid transparent}.qtip-titlebar .qtip-close{right:4px;top:50%;margin-top:-9px}* html .qtip-titlebar .qtip-close{top:16px}.qtip-icon .ui-icon,.qtip-titlebar .ui-icon{display:block;text-indent:-1000em;direction:ltr}.qtip-icon,.qtip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none}.qtip-icon .ui-icon{width:18px;height:14px;line-height:14px;text-align:center;text-indent:0;font:normal 700 10px/13px Tahoma,sans-serif;color:inherit;background:-100em -100em no-repeat}.qtip-default{border:1px solid #F1D031;background-color:#FFFFA3;color:#555}.qtip-default .qtip-titlebar{background-color:#FFEF93}.qtip-default .qtip-icon{border-color:#CCC;background:#F1F1F1;color:#777}.qtip-default .qtip-titlebar .qtip-close{border-color:#AAA;color:#111}.qtip .qtip-tip{margin:0 auto;overflow:hidden;z-index:10}.qtip .qtip-tip,x:-o-prefocus{visibility:hidden}.qtip .qtip-tip,.qtip .qtip-tip .qtip-vml,.qtip .qtip-tip canvas{position:absolute;color:#123456;background:0 0;border:0 dashed transparent}.qtip .qtip-tip canvas{top:0;left:0}.qtip .qtip-tip .qtip-vml{behavior:url(#default#VML);display:inline-block;visibility:visible}
756
-
757
- /*! jQuery UI - v1.10.3 | http://jqueryui.com */
758
- #ui-datepicker-div{display:none;}.ui-datepicker{background-color:#fff;border:1px solid #ccc;width:17em;padding:.2em .2em 0;display:none;}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month-year{width:100%}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:700;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}
759
-
760
- /*! bootstrap-switch - v3.3.2 | http://www.bootstrap-switch.org */
761
- .bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:inline-block!important;height:100%;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success{color:#fff;background:#5cb85c}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=radio],.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0 !important;filter:alpha(opacity=0)}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.3333333}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-indeterminate,.bootstrap-switch.bootstrap-switch-readonly{cursor:default!important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default!important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-radius:0 3px 3px 0}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-radius:3px 0 0 3px}.bootstrap-switch.bootstrap-switch-focused{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px}
762
-
763
- /* jQuery tagEditor v1.0.20 | https://github.com/Pixabay/jQuery-tagEditor */
 
764
  .tag-editor{list-style-type:none;padding:0 5px 0 0;margin:0;overflow:hidden;border:1px solid #eee;cursor:text;font:normal 14px sans-serif;color:#555;background:#fff;line-height:20px}.tag-editor li{display:block;float:left;overflow:hidden;margin:3px 0}.tag-editor div{float:left;padding:0 4px}.tag-editor .placeholder{padding:0 8px;color:#bbb}.tag-editor .tag-editor-spacer{padding:0;width:8px;overflow:hidden;color:transparent;background:none}.tag-editor input{vertical-align:inherit;border:0;outline:none;padding:0;margin:0;cursor:text;font-family:inherit;font-weight:inherit;font-size:inherit;font-style:inherit;box-shadow:none;background:none;color:#444}.tag-editor-hidden-src{position:absolute!important;left:-99999px}.tag-editor ::-ms-clear{display:none}.tag-editor .tag-editor-tag{padding-left:5px;color:#46799b;background:#e0eaf1;white-space:nowrap;overflow:hidden;cursor:pointer;border-radius:2px 0 0 2px}.tag-editor .tag-editor-delete{background:#e0eaf1;cursor:pointer;border-radius:0 2px 2px 0;padding-left:3px;padding-right:4px}.tag-editor .tag-editor-delete i{line-height:18px;display:inline-block}.tag-editor .tag-editor-delete i:before{font-size:16px;color:#8ba7ba;content:"×";font-style:normal}.tag-editor .tag-editor-delete:hover i:before{color:#d65454}.tag-editor .tag-editor-tag.active+.tag-editor-delete,.tag-editor .tag-editor-tag.active+.tag-editor-delete i{visibility:hidden;cursor:text}.tag-editor .tag-editor-tag.active{background:none!important}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default;font-size:14px}.ui-front{z-index:9999}.ui-menu{list-style:none;padding:1px;margin:0;display:block;outline:none}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.4;min-height:0}.ui-widget-content{border:1px solid #bbb;background:#fff;color:#555}.ui-widget-content a{color:#46799b}.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{background:#e0eaf1}.ui-helper-hidden-accessible{display:none}
1
+ /* Links */
2
+ .wrap.slimstat a, [id^=slim_] a{
3
+ outline: none;
4
+ text-decoration: none;
5
+ }
6
+ [id^=slim_] a.slimstat-delete-entry:before{
7
+ color: #e22;
8
+ }
9
+ .slimstat-float-right{
10
+ float: right;
11
+ }
12
+
13
+ /* Admin Notice */
14
+ .slimstat-notice{
15
+ overflow: hidden;
16
+ }
17
+ .slimstat-notice span{
18
+ width: 97%;
19
+ }
20
+ .slimstat-notice .fixed-height {
21
+ height: 120px;
22
+ margin-bottom: 20px;
23
+ overflow: auto;
24
+ padding: 0 20px 0 0;
25
+ }
26
+
27
+ /* Filters */
28
+ #slimstat-filters-post{
29
+ display: none;
30
+ }
31
+ #slimstat-filters-form{
32
+ background-color: #e0e0e0;
33
+ margin-top: 10px;
34
+ padding: 5px;
35
+ position: relative;
36
+ }
37
+ #slimstat-filters input.text, #slimstat-filters select{
38
+ vertical-align: initial;
39
+ width: 15%;
40
+ }
41
+ #slimstat-filters-form label {
42
+ left:-9000px;
43
+ position: absolute;
44
+ }
45
+ #slimstat-date-filters{
46
+ padding: 4px 10px 6px 20px;
47
+ position: absolute;
48
+ right: 5px;
49
+ top: 5px;
50
+ }
51
+ #slimstat-date-filters>a{
52
+ color: #fff;
53
+ white-space: nowrap;
54
+ }
55
+ #slimstat-date-filters>a::after{
56
+ content: "\f140";
57
+ font: normal 20px/1 'dashicons';
58
+ vertical-align: bottom;
59
+ }
60
+ #slimstat-date-filters>a.open::after{
61
+ content: "\f142";
62
+ }
63
+ #slimstat-date-filters .dropdown {
64
+ background-color: #fff;
65
+ box-shadow: 0 1px 8px rgba(0, 0, 0, 0.2);
66
+ color: #222;
67
+ display: none;
68
+ padding: 5px 0 5px 5px;
69
+ position: absolute;
70
+ right: -4px;
71
+ top: 35px;
72
+ width: 390px;
73
+ z-index: 120;
74
+ }
75
+ #slimstat-date-filters .dropdown #slimstat-quick-filters a {
76
+ background-color: #eee;
77
+ display: block;
78
+ float: left;
79
+ margin: 0 5px 5px 0;
80
+ padding: 5px;
81
+ width: 115px;
82
+ }
83
+ #slimstat-date-filters .dropdown strong{
84
+ clear: both;
85
+ display: block;
86
+ font-weight: normal;
87
+ padding: 10px 5px 5px 0;
88
+ text-transform: uppercase;
89
+ }
90
+ #slimstat-date-filters .dropdown select, #slimstat-date-filters .dropdown input{
91
+ margin: 0 5px 5px 0;
92
+ height: 27px;
93
+ width: 135px;
94
+ }
95
+ #slimstat-date-filters .dropdown select.short, #slimstat-date-filters .dropdown input.short{
96
+ width: 65px;
97
+ }
98
+ #slimstat-date-filters .dropdown .ui-datepicker-trigger{
99
+ float: right;
100
+ margin: 2px 5px 0 0;
101
+ }
102
+ #slimstat-current-filters{
103
+ background-color: #ccc;
104
+ border-top: 2px solid #eee;
105
+ color: #222;
106
+ font-size: 1.1em;
107
+ overflow: hidden;
108
+ padding: 5px;
109
+ position: relative
110
+ }
111
+ #slimstat-current-filters .slimstat-remove-filter{
112
+ color: #fff;
113
+ }
114
+ #slimstat-current-filters .slimstat-filter-list{
115
+ float: left;
116
+ margin: 0;
117
+ padding: 4px 0 5px 5px;;
118
+ }
119
+ #slimstat-current-filters .slimstat-filter-list li{
120
+ display: inline;
121
+ margin: 0 20px 0 0;
122
+ vertical-align: middle;
123
+ }
124
+ .slimstat-filter-action-button{
125
+ float: right;
126
+ margin-left: 10px !important;
127
+ }
128
+
129
+ /* Reports: Container */
130
+ .wrap.slimstat .meta-box-sortables {
131
+ margin-top: 20px;
132
+ }
133
+
134
+ /* Single Report */
135
+ .wrap.slimstat .postbox, .wrap.slimstat .sortable-placeholder {
136
+ box-sizing: border-box;
137
+ float: left;
138
+ margin-bottom: 7px;
139
+ margin-right: 0.5%;
140
+ min-width: 24.3%;
141
+ overflow: hidden;
142
+ width: 24.3%;
143
+ }
144
+ .wrap.slimstat .postbox.large {
145
+ width: 49.1%;
146
+ }
147
+ .wrap.slimstat .postbox.extralarge {
148
+ width: 73.9%;
149
+ }
150
+ .wrap.slimstat .postbox.full-width {
151
+ width: 98.7%;
152
+ }
153
+
154
+ .sortable-placeholder{
155
+ background-color: #ccc;
156
+ border: 1px dashed #bbb;
157
+ margin-bottom: 9px
158
+ }
159
+
160
+ /* Single Report: Header and Header Buttons */
161
+ .wrap.slimstat .postbox h3{
162
+ border-bottom: 1px solid #ddd;
163
+ font-size: 1.2em;
164
+ margin: 0;
165
+ padding: 10px;
166
+ }
167
+ .wrap.slimstat .postbox h3:hover:after {
168
+ color: #bbb;
169
+ content: attr(data-report-id);
170
+ margin-left: 10px;
171
+ }
172
+ .wrap.slimstat .postbox.tall h3{
173
+ cursor:auto;
174
+ }
175
+ .slimstat-header-buttons{
176
+ float: right;
177
+ padding: 10px 10px 0;
178
+ }
179
+ .slimstat-tooltip-trigger.corner{
180
+ background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAABkSURBVHjaYpw1a9YUBgaGbCD+AsR/GRCACYg/sACJXCBWBGIvBkzAC1L1H4i9gXgRFgUfmZA48UC8EF0FExo/AYgXIPH/MWExNhGI50PZnNgUgEASEG8FYg5cCkDAB4gnAAQYAGBCD4bcPf1fAAAAAElFTkSuQmCC) 0 0 no-repeat scroll;
181
+ cursor: help;
182
+ display: block;
183
+ height: 7px;
184
+ position: absolute;
185
+ top: 0;
186
+ width: 7px;
187
+ }
188
+ .slimstat-tooltip-content{
189
+ display: none;
190
+ }
191
+ .wrap.slimstat h3 .slimstat-tooltip-trigger.corner{
192
+ right: 0;
193
+ }
194
+ .slimstat .no-refresh .refresh{
195
+ display: none;
196
+ }
197
+
198
+ /* Single Report: Header and Header Buttons - Pagination */
199
+ [id^=slim_] p.pagination{
200
+ background-color: #e0e0e0;
201
+ border-bottom: 1px solid #cde;
202
+ border-top: 1px solid #cde;
203
+ color: #444;
204
+ line-height: 1em;
205
+ min-height: 5px;
206
+ }
207
+ [id^=slim_] p.pagination a{
208
+ color: #444;
209
+ float: right;
210
+ }
211
+ [id^=slim_] p.pagination a:hover{
212
+ text-shadow: 0 0 2px #000;
213
+ }
214
+
215
+ /* Single Report: Inside */
216
+ [id^=slim_] .inside{
217
+ height: 281px;
218
+ margin: 0 !important;
219
+ overflow: auto;
220
+ padding: 0 !important;
221
+ }
222
+ .wrap.slimstat .postbox.tall .inside{
223
+ height: 650px;
224
+ }
225
+ [id^=slim_] p {
226
+ border-bottom: 1px solid #ddd;
227
+ line-height: 1.5em;
228
+ margin: 0;
229
+ min-height: 20px;
230
+ overflow: auto;
231
+ padding: 7px 10px;
232
+ position: relative;
233
+ word-wrap: break-word;
234
+ }
235
+ [id^=slim_] p:last-child{
236
+ border-bottom:0;
237
+ }
238
+ [id^=slim_] p .slimstat-tooltip-trigger.corner{
239
+ -moz-transform: scaleX(-1);
240
+ -o-transform: scaleX(-1);
241
+ -webkit-transform: scaleX(-1);
242
+ transform: scaleX(-1);
243
+ filter: FlipH;
244
+ -ms-filter: "FlipH";
245
+ left: 0;
246
+ }
247
+ [id^=slim_] p.header{
248
+ background-color:#eee;
249
+ border-color:#a6a6a6;
250
+ }
251
+ [id^=slim_] p span{
252
+ float: right;
253
+ }
254
+ [id^=slim_] p span.pageview-screenres{
255
+ margin-right: 10px;
256
+ }
257
+ [id^=slim_] a:hover{
258
+ text-shadow: 0 0 2px #bbb;
259
+ }
260
+ [id^=slim_] .slimstat-tooltip-content.expanded{
261
+ display: block;
262
+ font-weight: normal;
263
+ }
264
+ [id^=slim_] .inline-icon{
265
+ background-color: transparent;
266
+ background-position: 0 0;
267
+ background-repeat: no-repeat;
268
+ display: inline-block;
269
+ height: 18px;
270
+ line-height: 18px;
271
+ margin-right: 5px;
272
+ vertical-align: middle;
273
+ width: 16px;
274
+ }
275
+ [id^=slim_] .spaced{
276
+ margin-left:15px;
277
+ }
278
+ #dashboard-widgets-wrap .whois{
279
+ display: none;
280
+ }
281
+
282
+ /* Single Report: Table */
283
+ [id^=slim_] .widefat th{
284
+ white-space: nowrap;
285
+ }
286
+ [id^=slim_] .widefat td{
287
+ border-bottom: 1px solid #e1e1e1;
288
+ }
289
+
290
+ /* Single Report: Debug Message */
291
+ [id^=slim_] .debug{
292
+ background-color: #000;
293
+ color: #fff;
294
+ display: block;
295
+ font-family: monospace;
296
+ opacity: .8;
297
+ padding: 20px;
298
+ position: relative;
299
+ }
300
+
301
+ /* Single Report: Color Coded Entries */
302
+ .little-color-box{
303
+ background-color: #eee;
304
+ border: 1px solid #aaa;
305
+ display: block;
306
+ float: left;
307
+ height: 15px;
308
+ margin-right: 10px;
309
+ width: 15px;
310
+ }
311
+ [id^=slim_] .header.is-search-engine, .little-color-box.is-search-engine{
312
+ background-color:#c1e751;
313
+ color:#444;
314
+ }
315
+ [id^=slim_] .header.is-direct, .little-color-box.is-direct{
316
+ background-color:#d0e0eb;
317
+ color:#111;
318
+ }
319
+ [id^=slim_] .header.is-known-user, .little-color-box.is-known-user{
320
+ background-color:#F1CF90;
321
+ }
322
+ [id^=slim_] .header.is-known-visitor, .little-color-box.is-known-visitor{
323
+ background-color:#EFFD8C;
324
+ }
325
+ [id^=slim_] .header.is-spam, .little-color-box.is-spam{
326
+ background-color:#AAB3AB;
327
+ color:#222;
328
+ }
329
+ [id^=slim_] p.loading{
330
+ font-size: 30px;
331
+ line-height: 4em;
332
+ margin-top: 60px;
333
+ text-align: center;
334
+ }
335
+ [id^=slim_] p.nodata{
336
+ border: 0;
337
+ color: #999;
338
+ padding-top: 95px;
339
+ text-align: center;
340
+ }
341
+
342
+ /* Single Report: Chart */
343
+ .chart-placeholder{
344
+ height: 280px;
345
+ }
346
+
347
+ /* Single Report: Modal Window */
348
+ .ui-widget-overlay{
349
+ background-color: #222;
350
+ filter: alpha(opacity=60);
351
+ height: 100%;
352
+ opacity: .6;
353
+ position: fixed;
354
+ top: 0;
355
+ width: 100%;
356
+ z-index: 100 !important;
357
+ }
358
+ .ui-dialog.slimstat{
359
+ border: 0;
360
+ border-radius: 0;
361
+ padding: 0;
362
+ width: 90% !important;
363
+ z-index: 100500 !important;
364
+ }
365
+ .ui-dialog.slimstat .ui-dialog-titlebar{
366
+ background: #4b8df8;
367
+ border: 0;
368
+ border-radius: 0;
369
+ color: #fff;
370
+ font-family: 'Open Sans', sans-serif;
371
+ font-size: 1.3em;
372
+ font-weight: normal;
373
+ height: 22px;
374
+ line-height: 1.3em;
375
+ margin: 0;
376
+ padding: 5px 10px;
377
+ }
378
+ .ui-dialog.slimstat .ui-dialog-titlebar-close{
379
+ background-color: transparent;
380
+ border: 0;
381
+ color: #fff;
382
+ float: right;
383
+ line-height: 1.3em;
384
+ padding: 0;
385
+ }
386
+ .ui-dialog.slimstat .ui-dialog-titlebar-close:before{
387
+ content:'\e803';
388
+ font-family: slimstat;
389
+ }
390
+ .ui-dialog .ui-dialog-content{
391
+ clear: both;
392
+ }
393
+ #slimstat-modal-dialog{
394
+ background-color: #fff;
395
+ display: none;
396
+ max-height: 650px !important;
397
+ overflow: auto;
398
+ padding: 0;
399
+ width: auto;
400
+ }
401
+ #slimstat-modal-dialog p{
402
+ margin: 0;
403
+ padding: 10px;
404
+ }
405
+
406
+ /* Dashboard Widgets */
407
+ #dashboard-widgets [id*=slim_p].postbox .inside {
408
+ height: 281px;
409
+ }
410
+ #dashboard-widgets [id*=slim_p][id*=_01].postbox .inside {
411
+ height: 290px;
412
+ }
413
+ #dashboard-widgets #slim_p7_02.postbox .inside {
414
+ height: 320px;
415
+ }
416
+ .closed .slimScrollDiv{
417
+ height: 0 !important;
418
+ }
419
+
420
+ /* Settings */
421
+ #slimstat-message{
422
+ background-color: #555;
423
+ clear: both;
424
+ color: #fff;
425
+ overflow: hidden;
426
+ padding: 0 10px;
427
+ }
428
+ #slimstat-message p:before{
429
+ content: "\f339";
430
+ font-family: 'dashicons';
431
+ margin-right: 10px;
432
+ vertical-align: bottom;
433
+ }
434
+ #slimstat-message.wp-ui-notification p:before{
435
+ content: "\f160";
436
+ }
437
+ #slimstat-message p{
438
+ line-height: 1em;
439
+ }
440
+ #slimstat-message a{
441
+ color: #fc0;
442
+ }
443
+ .nav-tabs{
444
+ margin: 20px 1px 0;
445
+ }
446
+ .nav-tab{
447
+ border-width: 1px 1px 0 !important;
448
+ font-size: 14px;
449
+ margin: 0 5px 0 0;
450
+ }
451
+ .nav-tab.nav-tab-active{
452
+ background-color: #fff !important;
453
+ }
454
+ .nav-tab a{
455
+ color: #444;
456
+ display: block;
457
+ text-decoration: none;
458
+ }
459
+ .form-table{
460
+ border: 1px solid #ccc !important;
461
+ margin-top: 0 !important;
462
+ }
463
+ .form-table th{
464
+ font-weight: 400 !important;
465
+ padding: 15px 10px !important;
466
+ }
467
+ .slimstat-options-section-header {
468
+ background-color: #e4e4e4;
469
+ font-size: 1.4em !important;
470
+ margin: 0;
471
+ padding: 5px 10px !important;
472
+ }
473
+ .form-table h3{
474
+ margin-top: 0;
475
+ }
476
+ .form-table td span.block-element{
477
+ padding: 0 30px 0 0;
478
+ }
479
+ .form-table .description{
480
+ color: #999;
481
+ display: block;
482
+ font-style: normal;
483
+ margin-top: 5px;
484
+ }
485
+ .form-table #slimstat-filter-name, .form-table #slimstat-filter-operator, .form-table #slimstat-filter-value{
486
+ width: 20%;
487
+ }
488
+ [id*=form-slimstat-options] input[type=number].small-text{
489
+ width: 85px;
490
+ }
491
+ [class*=bootstrap-switch-id-addon_network_settings] {
492
+ float: right;
493
+ }
494
+
495
+ /* Add-ons Page */
496
+ .wp-list-table.slimstat-addons{
497
+ margin-bottom: 20px;
498
+ }
499
+ .wp-list-table.slimstat-addons tbody th{
500
+ border-left: 5px solid #ccc;
501
+ }
502
+ .wp-list-table.slimstat-addons th,.wp-list-table.slimstat-addons td{
503
+ border-bottom: 1px solid #ccc;
504
+ }
505
+ .wp-list-table.slimstat-addons .active th{
506
+ border-color: #10a062;
507
+ border-style: solid;
508
+ border-width: 0 0 0 5px;
509
+ }
510
+ .wp-list-table.slimstat-addons .active td{
511
+ border: 0;
512
+ }
513
+
514
+ /* Edit Posts Page */
515
+ .column-wp-slimstat{
516
+ text-align: center !important;
517
+ width: 3em;
518
+ }
519
+ .slimstat-icon:before{
520
+ content: '\f239';
521
+ font-family: dashicons;
522
+ }
523
+
524
+ /* RTL Support */
525
+ .rtl #slimstat-current-filters .slimstat-filter-list{
526
+ padding: 0 0 0 75px;
527
+ }
528
+ .rtl #slimstat-current-filters .slimstat-filter-list li{
529
+ margin-left: 20px;
530
+ margin-right: 0;
531
+ }
532
+ .rtl #slimstat-remove-all-filters{
533
+ left: 5px;
534
+ right: inherit;
535
+ }
536
+ .rtl #slimstat-date-filters{
537
+ left: 5px;
538
+ right: inherit;
539
+ }
540
+ .rtl #slimstat-date-filters span{
541
+ left: 0;
542
+ right: inherit;
543
+ }
544
+ .rtl .wrap.slimstat .postbox{
545
+ float: right;
546
+ }
547
+ .rtl .slimScrollBar{
548
+ left: 2px !important;
549
+ right: inherit !important;
550
+ }
551
+ .rtl .slimstat-header-buttons{
552
+ float: left;
553
+ }
554
+ .rtl [id^=slim_] p.pagination a{
555
+ float: left;
556
+ }
557
+ .rtl [id^=slim_] p span{
558
+ float: left;
559
+ }
560
+ .rtl .qtip-content{
561
+ text-align: right;
562
+ }
563
+ .rtl .form-table td span.block-element{
564
+ padding: 0 0 0 30px;
565
+ }
566
+
567
+ /* Customizer */
568
+ .slimstat-layout .postbox-container {
569
+ border: 1px solid #ccc;
570
+ float: none;
571
+ margin-bottom: 20px;
572
+ overflow: hidden;
573
+ }
574
+ .slimstat-layout .meta-box-sortables {
575
+ overflow: hidden;
576
+ padding: 10px;
577
+ }
578
+ .slimstat-layout .postbox-container span.title {
579
+ background-color: #c0c0c0;
580
+ color: #fff;
581
+ font-size: 16px;
582
+ margin-bottom: 0;
583
+ padding: 10px;
584
+ }
585
+ .slimstat-layout .postbox, .slimstat-layout .sortable-placeholder {
586
+ float: left;
587
+ margin: 0 10px 10px 0;
588
+ min-width: 285px;
589
+ }
590
+ .slimstat-layout .postbox.invisible h3 {
591
+ color: #ccc;
592
+ }
593
+ .slimstat-layout h3 {
594
+ border: 0;
595
+ font-weight: 300;
596
+ font-size: 1.1em;
597
+ margin: 0;
598
+ padding: 5px 10px;
599
+ }
600
+ .slimstat-layout #postbox-container-slimview1 {
601
+ display: none;
602
+ }
603
+
604
+ /* Responsive */
605
+ @media screen and ( max-width: 1560px ) {
606
+ .wrap.slimstat .postbox {
607
+ width: 32.8%;
608
+ }
609
+ .wrap.slimstat .postbox.large {
610
+ width: 66.1%;
611
+ }
612
+ .wrap.slimstat .postbox.extralarge, .wrap.slimstat .postbox.full-width {
613
+ width: 99.4%;
614
+ }
615
+ .wrap.slimstat .postbox h3 {
616
+ font-size: 1em;
617
+ line-height: 1.5em;
618
+ }
619
+ .wrap.slimstat .postbox p {
620
+ font-size: 1em;
621
+ line-height: 1.5em;
622
+ }
623
+ }
624
+
625
+ @media screen and ( max-width: 1080px ) {
626
+ .wrap.slimstat .postbox{
627
+ width: 49.125%;
628
+ }
629
+ .wrap.slimstat .postbox.large, .wrap.slimstat .postbox.extralarge, .wrap.slimstat .postbox.full-width {
630
+ width: 99%;
631
+ }
632
+ }
633
+ @media screen and (max-width: 800px){
634
+ .slimstat-notice span {
635
+ width: 90%;
636
+ }
637
+ #slimstat-filters input.text, #slimstat-filters select {
638
+ margin: 0 .5% 0 0;
639
+ width: 26%;
640
+ }
641
+ .wp-core-ui .button-secondary {
642
+ height: 35px;
643
+ }
644
+ #slimstat-date-filters {
645
+ margin-top: 5px;
646
+ position: relative;
647
+ right: inherit;
648
+ top: inherit;
649
+ }
650
+ .wrap.slimstat .postbox, .wrap.slimstat .postbox.large, .wrap.slimstat .postbox.extralarge, .wrap.slimstat .postbox.full-width {
651
+ margin: 0 0 10px;
652
+ width: 99.5%;
653
+ }
654
+ .wrap.slimstat .postbox h3 {
655
+ font-size: 1.4em;
656
+ line-height: 1em;
657
+ }
658
+ .wrap.slimstat .postbox p {
659
+ font-size: 1.1em;
660
+ line-height: 1.4em;
661
+ }
662
+
663
+ /* Settings */
664
+ .nav-tab {
665
+ display: block;
666
+ margin: 0;
667
+ }
668
+ .form-table th {
669
+ font-size: 1.4em;
670
+ }
671
+ .form-table th label {
672
+ font-size: 1em;
673
+ }
674
+ .form-table td {
675
+ padding: 10px;
676
+ }
677
+ .form-table .button-primary,.form-table .button-secondary {
678
+ height: 30px;
679
+ margin-bottom: 5px;
680
+ text-align: center;
681
+ width: 100%;
682
+ }
683
+ .form-table #slimstat-filter-name, .form-table #slimstat-filter-operator, .form-table #slimstat-filter-value {
684
+ width: 100%;
685
+ }
686
+
687
+ /* Add-on: WP SlimStat - User Overview */
688
+ [id^='slim_'] .users .column-name {
689
+ display: none;
690
+ }
691
+ }
692
+
693
+ /* Fontello - Custom Icon Fonts for SlimStat | Uses Fontelico (C) 2012 by Fontello project, Elusive (C) 2013 by Aristeides Stathopoulos, Font Awesome (C) 2012 by Dave Gandy | http://www.fontello.com */
694
+ @font-face {
695
+ font-family: 'slimstat';
696
+ src: url('slimstat.eot?58272494');
697
+ src: url('slimstat.eot?58272494#iefix') format('embedded-opentype'),
698
+ url('slimstat.svg?58272494#fontello') format('svg');
699
+ font-weight: normal;
700
+ font-style: normal;
701
+ }
702
+ @font-face {
703
+ font-family: 'slimstat';
704
+ src: url('data:application/octet-stream;base64,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') format('woff'),
705
+ url('data:application/octet-stream;base64,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') format('truetype');
706
+ }
707
+ [class^="slimstat-font-"]:before, [class*=" slimstat-font-"]:before {
708
+ color: #aaa;
709
+ font-family: "slimstat";
710
+ font-style: normal;
711
+ font-weight: normal;
712
+ speak: none;
713
+
714
+ text-decoration: inherit;
715
+ width: 1em;
716
+ margin: 0 .2em;
717
+ text-align: center;
718
+
719
+ /* For safety - reset parent styles, that can break glyph codes*/
720
+ font-variant: normal;
721
+ text-transform: none;
722
+
723
+ /* fix buttons height, for twitter bootstrap */
724
+ line-height: 1em;
725
+ }
726
+ .slimstat-font-trash:before { content: '\e800'; } /* '' */
727
+ .slimstat-font-clock:before { content: '\e801'; } /* '' */
728
+ .slimstat-font-search:before { content: '\e802'; } /* '' */
729
+ .slimstat-font-cancel:before { content: '\e803'; } /* '' */
730
+ .slimstat-font-download:before { content: '\e804'; } /* '' */
731
+ .slimstat-font-upload:before { content: '\e805'; } /* '' */
732
+ .slimstat-font-download-cloud:before { content: '\e806'; } /* '' */
733
+ .slimstat-font-upload-cloud:before { content: '\e807'; } /* '' */
734
+ .slimstat-font-location-1:before { content: '\e808'; } /* '' */
735
+ .slimstat-font-arrows-cw-1:before { content: '\e809'; } /* '' */
736
+ .slimstat-font-login:before { content: '\e80a'; } /* '' */
737
+ .slimstat-font-logout:before { content: '\e80b'; } /* '' */
738
+ .slimstat-font-angle-left:before { content: '\e80c'; } /* '' */
739
+ .slimstat-font-angle-right:before { content: '\e80d'; } /* '' */
740
+ .slimstat-font-angle-double-left:before { content: '\e80e'; } /* '' */
741
+ .slimstat-font-angle-double-right:before { content: '\e80f'; } /* '' */
742
+ .slimstat-font-arrows-cw:before { content: '\e810'; } /* '' */
743
+ .slimstat-font-doc:before { content: '\e811'; } /* '' */
744
+ .slimstat-font-spin4:before { content: '\e812'; } /* '' */
745
+ .slimstat-font-edit:before { content: '\e813'; } /* '' */
746
+ .slimstat-font-gauge:before { content: '\e814'; } /* '' */
747
+ .slimstat-font-stopwatch:before { content: '\e815'; } /* '' */
748
+ .slimstat-font-docs:before { content: '\e816'; } /* '' */
749
+ .slimstat-font-user-plus:before { content: '\e817'; } /* '' */
750
+ .slimstat-font-user-times:before { content: '\e818'; } /* '' */
751
+ .slimstat-font-minus-circled:before { content: '\e819'; } /* '' */
752
+
753
+ .animate-spin{-moz-animation:spin 3s infinite linear;-o-animation:spin 3s infinite linear;-webkit-animation:spin 3s infinite linear;animation:spin 3s infinite linear;display:inline-block;line-height:1em;}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-o-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-ms-keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spin{0%{-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(359deg);-o-transform:rotate(359deg);-webkit-transform:rotate(359deg);transform:rotate(359deg)}}
754
+
755
+ /* qTip2 v3.0.3 basic | http://qtip2.com */
756
+ .qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:1em;line-height:1.5em;direction:ltr;box-shadow:none;border-radius:5px;padding:0}.qtip-content,.qtip-titlebar{position:relative;overflow:hidden}.qtip-content{padding:5px 9px;text-align:left;word-wrap:break-word}.qtip-titlebar{padding:5px 35px 5px 10px;border-width:0 0 1px;font-weight:700}.qtip-titlebar+.qtip-content{border-top-width:0!important}.qtip-close{position:absolute;right:-9px;top:-9px;z-index:11;cursor:pointer;outline:0;border:1px solid transparent}.qtip-titlebar .qtip-close{right:4px;top:50%;margin-top:-9px}* html .qtip-titlebar .qtip-close{top:16px}.qtip-icon .ui-icon,.qtip-titlebar .ui-icon{display:block;text-indent:-1000em;direction:ltr}.qtip-icon,.qtip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none}.qtip-icon .ui-icon{width:18px;height:14px;line-height:1.5em;text-align:center;text-indent:0;font:normal 700 1em Tahoma,sans-serif;color:inherit;background:-100em -100em no-repeat}.qtip-default{border:1px solid #555;background-color:rgba(0,0,0,.9);color:#ddd}.qtip-default .qtip-titlebar{background-color:#FFEF93}.qtip-default .qtip-icon{border-color:#CCC;background:#F1F1F1;color:#777}.qtip-default .qtip-titlebar .qtip-close{border-color:#AAA;color:#111}.qtip .qtip-tip{margin:0 auto;overflow:hidden;z-index:10}.qtip .qtip-tip,x:-o-prefocus{visibility:hidden}.qtip .qtip-tip,.qtip .qtip-tip .qtip-vml,.qtip .qtip-tip canvas{position:absolute;color:#123456;background:0 0;border:0 dashed transparent}.qtip .qtip-tip canvas{top:0;left:0}.qtip .qtip-tip .qtip-vml{behavior:url(#default#VML);display:inline-block;visibility:visible}
757
+
758
+ /*! jQuery UI - v1.10.3 | http://jqueryui.com */
759
+ #ui-datepicker-div{display:none;}.ui-datepicker{background-color:#fff;border:1px solid #ccc;width:17em;padding:.2em .2em 0;display:none;}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month-year{width:100%}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:700;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}
760
+
761
+ /*! bootstrap-switch - v3.3.2 | http://www.bootstrap-switch.org */
762
+ .bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:inline-block!important;height:100%;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success{color:#fff;background:#5cb85c}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=radio],.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0 !important;filter:alpha(opacity=0)}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.3333333}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-indeterminate,.bootstrap-switch.bootstrap-switch-readonly{cursor:default!important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default!important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-radius:0 3px 3px 0}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-radius:3px 0 0 3px}.bootstrap-switch.bootstrap-switch-focused{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px}
763
+
764
+ /* jQuery tagEditor v1.0.20 | https://github.com/Pixabay/jQuery-tagEditor */
765
  .tag-editor{list-style-type:none;padding:0 5px 0 0;margin:0;overflow:hidden;border:1px solid #eee;cursor:text;font:normal 14px sans-serif;color:#555;background:#fff;line-height:20px}.tag-editor li{display:block;float:left;overflow:hidden;margin:3px 0}.tag-editor div{float:left;padding:0 4px}.tag-editor .placeholder{padding:0 8px;color:#bbb}.tag-editor .tag-editor-spacer{padding:0;width:8px;overflow:hidden;color:transparent;background:none}.tag-editor input{vertical-align:inherit;border:0;outline:none;padding:0;margin:0;cursor:text;font-family:inherit;font-weight:inherit;font-size:inherit;font-style:inherit;box-shadow:none;background:none;color:#444}.tag-editor-hidden-src{position:absolute!important;left:-99999px}.tag-editor ::-ms-clear{display:none}.tag-editor .tag-editor-tag{padding-left:5px;color:#46799b;background:#e0eaf1;white-space:nowrap;overflow:hidden;cursor:pointer;border-radius:2px 0 0 2px}.tag-editor .tag-editor-delete{background:#e0eaf1;cursor:pointer;border-radius:0 2px 2px 0;padding-left:3px;padding-right:4px}.tag-editor .tag-editor-delete i{line-height:18px;display:inline-block}.tag-editor .tag-editor-delete i:before{font-size:16px;color:#8ba7ba;content:"×";font-style:normal}.tag-editor .tag-editor-delete:hover i:before{color:#d65454}.tag-editor .tag-editor-tag.active+.tag-editor-delete,.tag-editor .tag-editor-tag.active+.tag-editor-delete i{visibility:hidden;cursor:text}.tag-editor .tag-editor-tag.active{background:none!important}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default;font-size:14px}.ui-front{z-index:9999}.ui-menu{list-style:none;padding:1px;margin:0;display:block;outline:none}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.4;min-height:0}.ui-widget-content{border:1px solid #bbb;background:#fff;color:#555}.ui-widget-content a{color:#46799b}.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{background:#e0eaf1}.ui-helper-hidden-accessible{display:none}
admin/js/slimstat.admin.js CHANGED
@@ -1,518 +1,521 @@
1
- if ( typeof SlimStatAdminParams == 'undefined' ) {
2
- SlimStatAdminParams = {
3
- refresh_interval: 0,
4
- expand_details: 'no',
5
- datepicker_image: '',
6
- text_direction: ''
7
- };
8
- }
9
-
10
- // ----- TABLE OF CONTENTS -----------------------------------------------------------
11
- //
12
- // 1. Data refresh
13
- // 2. Filters
14
- // 3. Activity log
15
- // 4. Customizer
16
- // 5. Miscellaneous
17
- // 6. Init third-party libraries
18
- //
19
- // -----------------------------------------------------------------------------------
20
-
21
- jQuery( function() {
22
-
23
- // ----- BEGIN: DATA REFRESH -----------------------------------------------------
24
- //
25
-
26
- // Reload a report's data if it is (re)activated via the checkbox under Screen Options
27
- jQuery( 'input.hide-postbox-tog[id^=slim_p]' ).on( 'click.postboxes', function () {
28
- if ( jQuery( this ).prop( "checked" ) && jQuery( '#' + jQuery( this ).val() ).length ) {
29
- SlimStatAdmin.refresh_report( jQuery( this ).val() );
30
- }
31
- });
32
-
33
- // Reload a report's data if the corresponding refresh/pagination button is clicked
34
- jQuery( document ).on( 'click', '[id^=slim_] .refresh', function( e ) {
35
- e.preventDefault();
36
-
37
- var id = jQuery( this ).parents( '.postbox' ).attr( 'id' );
38
-
39
- // Is this a pagination link?
40
- if ( typeof jQuery( this ).attr( 'href' ).split( '?' )[ 1 ] == 'string' ) {
41
- clean_filters = SlimStatAdmin.get_query_string_filters( jQuery( this ).attr( 'href' ).split( '?' )[ 1 ].substring( 1 ) );
42
- if ( typeof clean_filters[ 'fs[start_from]' ] == 'string' ) {
43
- jQuery( '<input type="hidden" name="fs[start_from]" class="slimstat-post-filter slimstat-temp-filter" value="' + clean_filters[ 'fs[start_from]' ] + '">' ).appendTo( '#slimstat-filters-form' );
44
- }
45
- }
46
-
47
- SlimStatAdmin.refresh_report( id );
48
-
49
- // Remove any temporary filters (pagination) set here above
50
- jQuery( '.slimstat-temp-filter' ).remove();
51
-
52
- // Re-initialize SlimScroll on the new content
53
- jQuery( '#' + id + ' .inside' ).slimScroll( {
54
- scrollTo : '0px'
55
- } );
56
- } );
57
-
58
- // Asynchronous reports are loaded dynamically after the page loads
59
- if ( SlimStatAdminParams.async_load == 'on' ) {
60
- jQuery( 'div[id^=slim_]' ).each( function() {
61
- SlimStatAdmin.refresh_report( jQuery( this ).attr( 'id' ) );
62
- } );
63
- }
64
-
65
- //
66
- // ----- END: DATA REFRESH -------------------------------------------------------
67
-
68
- // ----- BEGIN: FILTERS ----------------------------------------------------------
69
- //
70
-
71
- // Make input field read-only if certain operators are selected
72
- jQuery( '#slimstat-filter-operator' ).on( 'change', function() {
73
- if ( this.value == 'is_empty' || this.value == 'is_not_empty' ) {
74
- jQuery( '#slimstat-filter-value' ).attr( 'readonly', 'readonly' );
75
- }
76
- else {
77
- jQuery( '#slimstat-filter-value' ).removeAttr( 'readonly' );
78
- }
79
- } );
80
-
81
- // Toggle the Date Filters dropdown menu
82
- jQuery( '#slimstat-date-filters > a' ).on( 'click', function( e ) {
83
- e.preventDefault();
84
- jQuery( '#slimstat-date-filters > .dropdown' ).slideToggle( 250 );
85
- } );
86
-
87
- // Initialize the datepicker library and button (built-in)
88
- if ( typeof jQuery( '.slimstat-filter-date' ).datepicker == 'function' ) {
89
- jQuery( '.slimstat-filter-date' ).datepicker( {
90
- buttonImage: SlimStatAdminParams.datepicker_image,
91
- buttonImageOnly: true,
92
- changeMonth: true,
93
- changeYear: true,
94
- dateFormat: 'yy-m-d',
95
- maxDate: new Date,
96
- nextText: '&raquo;',
97
- prevText: '&laquo;',
98
- showOn: 'both',
99
-
100
- onClose: function( dateText, inst ) {
101
- if ( dateText.length ) {
102
- jQuery( '#slimstat-filter-day' ).val( dateText.split( '-' )[ 2 ] );
103
- jQuery( '#slimstat-filter-month' ).val( dateText.split( '-' )[ 1 ] );
104
- jQuery( '#slimstat-filter-year' ).val( dateText.split( '-' )[ 0 ] );
105
- }
106
- }
107
- } );
108
- }
109
-
110
- // Save filters
111
- jQuery( document ).on( 'click', '#slimstat-save-filter', function( e ) {
112
- e.preventDefault();
113
-
114
- data = {
115
- action: 'slimstat_manage_filters',
116
- security: jQuery( '#meta-box-order-nonce' ).val(),
117
- type: 'save',
118
- filter_array: jQuery( this ).attr( 'data-filter-array' )
119
- };
120
-
121
- var element = jQuery( this );
122
- jQuery.post( ajaxurl, data, function( response ) {
123
- element.text( response ).fadeOut( 1500 );
124
- } );
125
- } );
126
-
127
- // Load saved filters: open a dialog with a list of filters
128
- jQuery( document ).on( 'click', '#slimstat-load-saved-filters', function( e ) {
129
- e.preventDefault();
130
- var inner_html = '';
131
-
132
- data = {
133
- action: 'slimstat_manage_filters',
134
- security: jQuery( '#meta-box-order-nonce' ).val(),
135
- type: 'load',
136
- page: SlimStatAdmin.get_current_tab()
137
- };
138
-
139
- var dialog_title = jQuery( this ).attr( 'title' ); // passed as an attribute so that it can be localized
140
- jQuery.post( ajaxurl, data, function( response ) {
141
- jQuery( '#slimstat-modal-dialog' ).dialog( {
142
- dialogClass: 'slimstat',
143
- title: dialog_title
144
- } ).html( response ).dialog( 'open' );
145
- } );
146
- } );
147
-
148
- // Delete saved filters
149
- jQuery( document ).on( 'click', '.slimstat-delete-filter', function( e ) {
150
- e.preventDefault();
151
-
152
- data = {
153
- action: 'slimstat_manage_filters',
154
- security: jQuery( '#meta-box-order-nonce' ).val(),
155
- page: SlimStatAdmin.get_current_tab(),
156
- type: 'delete',
157
- filter_id: jQuery( this ).attr( 'data-filter-id' )
158
- };
159
-
160
- jQuery.post( ajaxurl, data, function( response ) {
161
- jQuery( '#slim_filters_overlay' ).parent().html( response );
162
- } );
163
- } );
164
-
165
- // Since we handle all "filter links" as POST requests (see code here above), we need to add dummy form tag to the dashboard,
166
- // for when our reports are displayed on that page
167
- if ( !jQuery( '#slimstat-filters-form' ).length ) {
168
- jQuery( '<form id="slimstat-filters-form" method="post"/>' ).appendTo('body');
169
- }
170
-
171
-
172
- jQuery( document ).on( 'click', '.slimstat-filter-link, #toplevel_page_slimview1 a, #wp-admin-bar-slimstat-header li a', function( e ) {
173
- url = jQuery( this ).attr( 'href' );
174
-
175
- // If this link doesn't have a valid HREF attribute, bail
176
- if ( typeof url != 'string' ) {
177
- return true;
178
- }
179
-
180
- e.preventDefault();
181
-
182
- jQuery( 'form#slimstat-filters-form' ).attr( 'action', url.split( '?' )[ 0 ] + '?page=' + SlimStatAdmin.get_current_tab( url.split( '?' )[ 1 ] ) );
183
-
184
- SlimStatAdmin.add_url_filters_to_form( url, typeof jQuery( this ).attr( 'data-reset-filters' ) != 'undefined' );
185
-
186
- jQuery( '#slimstat-filters-form' ).submit();
187
-
188
- return false;
189
- });
190
-
191
- //
192
- // ----- END: FILTERS ------------------------------------------------------------
193
-
194
- // ----- BEGIN: ACTIVITY LOG -----------------------------------------------------
195
- //
196
-
197
- // Reload the Activity Log every X seconds
198
- if ( SlimStatAdminParams.refresh_interval > 0 && jQuery( '.refresh-timer' ).length > 0 ) {
199
- SlimStatAdmin._refresh_timer = parseInt( SlimStatAdminParams.refresh_interval );
200
- window.setTimeout( "SlimStatAdmin.refresh_countdown();", 1000 );
201
- }
202
-
203
- // Delete a pageview when the corresponding button is clicked.
204
- // Since this content can be reloaded dynamically, we use the .on call with the classname
205
- jQuery( document ).on( 'click', '.slimstat-delete-entry', function( e ) {
206
- e.preventDefault();
207
-
208
- data = {
209
- action: 'slimstat_delete_pageview',
210
- security: jQuery( '#meta-box-order-nonce' ).val(),
211
- pageview_id : jQuery( this ).attr( 'data-pageview-id' )
212
- };
213
-
214
- var parent = jQuery( this ).parents( 'p' );
215
- jQuery.post( ajaxurl, data, function( response ) {
216
- parent.fadeOut( 500 );
217
- } );
218
- } );
219
-
220
- // // Modal Window / Whois
221
- jQuery( document ).on( 'click', '.whois', function( e ) {
222
- e.preventDefault();
223
-
224
- // If admin is using HTTPS and IP lookup service is not, open a new window/tab, instead of an overlay dialog
225
- if ( document.location.href.substr( 0, document.location.href.indexOf( "://" ) ).toLowerCase() == 'https' && jQuery( this ).attr( 'href' ).substr( 0, jQuery( this ).attr( 'href' ).indexOf( "://" ) ).toLowerCase() == 'http' ) {
226
- window.open( jQuery( this ).attr( 'href' ), '_blank' );
227
- return -1;
228
- }
229
-
230
- jQuery('#slimstat-modal-dialog').dialog({
231
- dialogClass: 'slimstat',
232
- title: jQuery(this).attr('title')
233
- }).html('<iframe id="ip2location" src="'+jQuery(this).attr('href')+'" width="100%" height="630"></iframe>');
234
- jQuery('#slimstat-modal-dialog').dialog('open');
235
- });
236
-
237
- //
238
- // ----- END: ACTIVITY LOG -------------------------------------------------------
239
-
240
- // ----- BEGIN: CUSTOMIZER -------------------------------------------------------
241
- //
242
-
243
- // Clone and delete report placeholders
244
- jQuery( '.slimstat-layout .slimstat-header-buttons a' ).on( 'click', function() {
245
- if ( jQuery( this ).hasClass( 'slimstat-font-docs' ) ) {
246
- jQuery( this ).removeClass( 'slimstat-font-docs' ).addClass( 'slimstat-font-trash' ).parents( '.postbox' ).clone( true ).appendTo( jQuery( this ).parents( '.meta-box-sortables' ) );
247
- jQuery( this ).removeClass( 'slimstat-font-trash' ).addClass( 'slimstat-font-docs' );
248
- }
249
- else if ( jQuery( this ).hasClass( 'slimstat-font-minus-circled' ) ) {
250
- jQuery( this ).removeClass( 'slimstat-font-minus-circled' ).parents( '.postbox' ).appendTo( jQuery( '#postbox-container-inactive .meta-box-sortables' ) );
251
- }
252
- else {
253
- jQuery( this ).parents( '.postbox' ).remove();
254
- }
255
-
256
- // Save the new groups
257
- var data = {
258
- action: 'meta-box-order',
259
- _ajax_nonce: jQuery( '#meta-box-order-nonce' ).val(),
260
- page: 'slimstat_page_slimlayout',
261
- page_columns: 0
262
- };
263
-
264
- jQuery( '.meta-box-sortables' ).each( function() {
265
- data[ 'order[' + this.id.split("-")[0] + ']' ] = jQuery( this ).sortable( 'toArray' ).join( ',' );
266
- });
267
-
268
- jQuery.post( ajaxurl, data );
269
- } );
270
-
271
- //
272
- // ----- END: CUSTOMIZER ---------------------------------------------------------
273
-
274
- // ----- BEGIN: MISCELLANEOUS ----------------------------------------------------
275
- //
276
-
277
- // Hide a notice and send the corresponding ajax request to the server
278
- jQuery( document ).on( 'click', '[id^=slimstat-hide-]', function( e ) {
279
- e.preventDefault();
280
- jQuery( this ).parents( '.slimstat-notice' ).slideUp( 1000 );
281
- data = {
282
- action: jQuery( this ).attr('id').replace(/-/g, '_'),
283
- security: jQuery( '#meta-box-order-nonce' ).val()
284
- };
285
-
286
- jQuery.ajax({
287
- url: ajaxurl,
288
- type: 'post',
289
- async: true,
290
- data: data
291
- });
292
- });
293
-
294
- //
295
- // ----- END: MISCELLANEOUS ------------------------------------------------------
296
-
297
- // ----- BEGIN: INIT THIRD-PARTY LIBRARIES ---------------------------------------
298
- //
299
-
300
- // SlimScroll
301
- jQuery( '[id^=slim_] .inside' ).slimScroll( {
302
- distance: '2px',
303
- opacity: '0.15',
304
- size: '5px',
305
- wheelStep: 10
306
- } );
307
-
308
- // QTip
309
- jQuery( document ).on( 'mouseover', '.slimstat-tooltip-trigger', function( e ) {
310
- if ( typeof jQuery( this ).attr( 'data-hasqtip' ) != 'undefined' ) {
311
- return true;
312
- }
313
-
314
- tooltip_content = jQuery( this ).find( '.slimstat-tooltip-content:not(.expanded)' );
315
-
316
- if ( typeof tooltip_content == 'undefined' || tooltip_content == null || tooltip_content.length == 0 ) {
317
- tooltip_content = jQuery( this ).attr( 'title' );
318
- }
319
- else {
320
- tooltip_content = tooltip_content[ 0 ].innerHTML;
321
- }
322
-
323
- if ( typeof tooltip_content != 'undefined' && tooltip_content.length ) {
324
- jQuery(this).qtip( {
325
- overwrite: false,
326
- content: {
327
- text: tooltip_content
328
- },
329
- show: {
330
- event: e.type,
331
- ready: true
332
- },
333
- hide: {
334
- delay: 250,
335
- fixed: true
336
- },
337
- position: {
338
- my: 'bottom right',
339
- at: 'top left',
340
- adjust: {
341
- x: 5
342
- },
343
- viewport: jQuery( window )
344
- },
345
- style: {
346
- classes: 'qtip-light'
347
- }
348
- }, e );
349
- }
350
- } );
351
-
352
- // Modal Window
353
- if ( typeof jQuery( '#slimstat-modal-dialog' ).dialog == 'function' ) {
354
- jQuery( '#slimstat-modal-dialog' ).dialog( {
355
- autoOpen: false,
356
- closeOnEscape: true,
357
- closeText: '',
358
- draggable: true,
359
- modal: true,
360
- open: function(){
361
- jQuery( '.ui-widget-overlay, .close-dialog' ).on( 'click', function() {
362
- jQuery( '#slimstat-modal-dialog' ).dialog( 'close' );
363
- } );
364
- },
365
- position: { my: "top center" },
366
- resizable: false
367
- } );
368
- }
369
-
370
- // BootstrapSwitch (pretty checkboxes in settings)
371
- jQuery( 'input.slimstat-checkbox-toggle' ).not( '[data-switch-no-init]' ).bootstrapSwitch();
372
-
373
- // TagEditor
374
- jQuery( 'textarea.slimstat-taglist' ).tagEditor( {
375
- forceLowercase: false
376
- } );
377
-
378
- //
379
- // ----- END: INIT THIRD-PARTY LIBRARIES -----------------------------------------
380
- } );
381
-
382
- // ----- BEGIN: SLIMSTATADMIN HELPER FUNCTIONS ---------------------------------------
383
- var SlimStatAdmin = {
384
- _refresh_timer: 0,
385
-
386
- refresh_countdown: function() {
387
- SlimStatAdmin._refresh_timer--;
388
- minutes = parseInt( SlimStatAdmin._refresh_timer / 60 );
389
- seconds = parseInt( SlimStatAdmin._refresh_timer % 60 );
390
-
391
- jQuery( '.refresh-timer' ).html( minutes + ':' + ( ( seconds < 10 ) ? '0' : '' ) + seconds );
392
-
393
- if ( SlimStatAdmin._refresh_timer > 0 ) {
394
- window.setTimeout( SlimStatAdmin.refresh_countdown, 1000 );
395
- }
396
- else {
397
- // Request the data from the server
398
- SlimStatAdmin.refresh_report( 'slim_p7_02' );
399
-
400
- // Reset the countdown timer
401
- SlimStatAdmin._refresh_timer = parseInt( SlimStatAdminParams.refresh_interval );
402
- window.setTimeout( "SlimStatAdmin.refresh_countdown();", 1000 );
403
- }
404
- },
405
-
406
- refresh_report: function( id ) {
407
- var inner_content = '#' + id + ' .inside';
408
- jQuery( inner_content ).html( '<p class="loading"><i class="slimstat-font-spin4 animate-spin"></i></p>' );
409
- data = {
410
- action: 'slimstat_load_report',
411
- security: jQuery( '#meta-box-order-nonce' ).val(),
412
- page: SlimStatAdmin.get_current_tab(),
413
- report_id: id
414
- };
415
-
416
- // Append the data from the hidden form
417
- filters_input = jQuery( '#slimstat-filters-form .slimstat-post-filter' ).toArray();
418
- for ( i in filters_input ) {
419
- data[ filters_input[ i ][ 'name' ] ] = filters_input[ i ][ 'value' ];
420
- }
421
-
422
- jQuery.post( ajaxurl, data, function( response ) {
423
- // Charts don't play nice with the "fade" animation we have for the other reports
424
- if ( id.indexOf( '_01' ) > 0 ) {
425
- jQuery( inner_content ).html( response );
426
- }
427
- else{
428
- jQuery( inner_content ).fadeOut( 500, function() { jQuery( this ).html( response ).fadeIn( 500 ); } );
429
-
430
- // If we are refreshing the Activity Log, let's reset the countdown timer
431
- if ( id == 'slim_p7_02' ) {
432
- SlimStatAdmin._refresh_timer = SlimStatAdminParams.refresh_interval;
433
- }
434
- }
435
- } );
436
- },
437
-
438
- get_query_string_filters: function( url ) {
439
-
440
- // Parse the URL and update the form_filters object accordingly
441
- query_string_pairs = url.substring( url.indexOf( '?' ) + 1 ).split( '&' );
442
- clean_filters = {};
443
-
444
- for ( i in query_string_pairs ) {
445
- decoded_pair = decodeURIComponent( query_string_pairs[ i ].replace( /\+/g, '%20' ) );
446
-
447
- if ( decoded_pair.indexOf( 'fs[' ) == -1 ) {
448
- continue;
449
- }
450
-
451
- a_pair = decoded_pair.split( '=' );
452
- if ( a_pair[ 0 ].length ) {
453
- clean_filters[ a_pair[ 0 ] ] = a_pair[ 1 ];
454
- }
455
- }
456
-
457
- return clean_filters;
458
- },
459
-
460
- add_url_filters_to_form: function( url, delete_existing_filters ) {
461
- clean_filters = SlimStatAdmin.get_query_string_filters( url );
462
-
463
- // Manipulate the existing list of filters (hidden input fields), if we don't want to delete them
464
- if ( typeof delete_existing_filters == 'undefined' || !delete_existing_filters ) {
465
-
466
- for( i in clean_filters ) {
467
- // If value is empty (length is 1, meaning that it just has the operator but no value), delete corresponding input field
468
- if ( clean_filters[ i ].trim().split( ' ' ).length == 1 ) {
469
- jQuery( 'input[name="' + i + '"]' ).remove();
470
- }
471
- else if ( jQuery( 'input[name="' + i + '"]' ).length > 0 ) {
472
- jQuery( 'input[name="' + i + '"]' ).attr( 'value', clean_filters[ i ] );
473
- }
474
- else {
475
- jQuery( '<input type="hidden" name="' + i + '" class="slimstat-post-filter" value="' + clean_filters[ i ] + '">' ).appendTo( '#slimstat-filters-form' );
476
- }
477
- }
478
- }
479
- // Start from a clean slate
480
- else {
481
- jQuery( '.slimstat-post-filter' ).remove();
482
-
483
- for( i in clean_filters ) {
484
- jQuery( '<input type="hidden" name="' + i + '" class="slimstat-post-filter" value="' + clean_filters[ i ] + '">' ).appendTo( '#slimstat-filters-form' );
485
- }
486
- }
487
- },
488
-
489
- // Get the value of a given key in the query string passed via the URL
490
- get_current_tab: function( query_string ) {
491
- query_string = typeof query_string == 'undefined' ? window.location.search.substring( 1 ) : query_string;
492
- var regex = new RegExp( "page(=([^&#]*)|&|#|$)" );
493
-
494
- values = regex.exec( query_string );
495
-
496
- if ( typeof values != 'undefined' && values != null && typeof values[ 2 ] == 'string' ) {
497
- return decodeURIComponent( values[ 2 ].replace( /\+/g, " " ) );
498
- }
499
-
500
- return '';
501
- }
502
- }
503
- // ----- END: SLIMSTATADMIN HELPER FUNCTIONS -----------------------------------------
504
-
505
- /* SlimScroll v1.3.8 | http://rocha.la | Copyright (c) 2011 Piotr Rochala. Released under the MIT and GPL licenses. */
506
- !function(e){e.fn.extend({slimScroll:function(i){var s={width:"auto",size:"7px",color:"#000",position:"right",distance:"1px",start:"top",opacity:.4,alwaysVisible:!1,disableFadeOut:!1,railVisible:!1,railColor:"#333",railOpacity:.2,railDraggable:!0,railClass:"slimScrollRail",barClass:"slimScrollBar",wrapperClass:"slimScrollDiv",allowPageScroll:!1,wheelStep:20,touchScrollStep:200,borderRadius:"7px",railBorderRadius:"7px"},o=e.extend(s,i);return this.each(function(){function s(t){if(h){var t=t||window.event,i=0;t.wheelDelta&&(i=-t.wheelDelta/120),t.detail&&(i=t.detail/3);var s=t.target||t.srcTarget||t.srcElement;e(s).closest("."+o.wrapperClass).is(x.parent())&&r(i,!0),t.preventDefault&&!y&&t.preventDefault(),y||(t.returnValue=!1)}}function r(e,t,i){y=!1;var s=e,r=x.outerHeight()-D.outerHeight();if(t&&(s=parseInt(D.css("top"))+e*parseInt(o.wheelStep)/100*D.outerHeight(),s=Math.min(Math.max(s,0),r),s=e>0?Math.ceil(s):Math.floor(s),D.css({top:s+"px"})),v=parseInt(D.css("top"))/(x.outerHeight()-D.outerHeight()),s=v*(x[0].scrollHeight-x.outerHeight()),i){s=e;var a=s/x[0].scrollHeight*x.outerHeight();a=Math.min(Math.max(a,0),r),D.css({top:a+"px"})}x.scrollTop(s),x.trigger("slimscrolling",~~s),n(),c()}function a(e){window.addEventListener?(e.addEventListener("DOMMouseScroll",s,!1),e.addEventListener("mousewheel",s,!1)):document.attachEvent("onmousewheel",s)}function l(){f=Math.max(x.outerHeight()/x[0].scrollHeight*x.outerHeight(),m),D.css({height:f+"px"});var e=f==x.outerHeight()?"none":"block";D.css({display:e})}function n(){if(l(),clearTimeout(p),v==~~v){if(y=o.allowPageScroll,b!=v){var e=0==~~v?"top":"bottom";x.trigger("slimscroll",e)}}else y=!1;return b=v,f>=x.outerHeight()?void(y=!0):(D.stop(!0,!0).fadeIn("fast"),void(o.railVisible&&R.stop(!0,!0).fadeIn("fast")))}function c(){o.alwaysVisible||(p=setTimeout(function(){o.disableFadeOut&&h||u||d||(D.fadeOut("slow"),R.fadeOut("slow"))},1e3))}var h,u,d,p,g,f,v,b,w="<div></div>",m=30,y=!1,x=e(this);if(x.parent().hasClass(o.wrapperClass)){var C=x.scrollTop();if(D=x.siblings("."+o.barClass),R=x.siblings("."+o.railClass),l(),e.isPlainObject(i)){if("height"in i&&"auto"==i.height){x.parent().css("height","auto"),x.css("height","auto");var H=x.parent().parent().height();x.parent().css("height",H),x.css("height",H)}else if("height"in i){var S=i.height;x.parent().css("height",S),x.css("height",S)}if("scrollTo"in i)C=parseInt(o.scrollTo);else if("scrollBy"in i)C+=parseInt(o.scrollBy);else if("destroy"in i)return D.remove(),R.remove(),void x.unwrap();r(C,!1,!0)}}else if(!(e.isPlainObject(i)&&"destroy"in i)){o.height="auto"==o.height?x.parent().height():o.height;var E=e(w).addClass(o.wrapperClass).css({position:"relative",overflow:"hidden",width:o.width,height:o.height});x.css({overflow:"hidden",width:o.width,height:o.height});var R=e(w).addClass(o.railClass).css({width:o.size,height:"100%",position:"absolute",top:0,display:o.alwaysVisible&&o.railVisible?"block":"none","border-radius":o.railBorderRadius,background:o.railColor,opacity:o.railOpacity,zIndex:90}),D=e(w).addClass(o.barClass).css({background:o.color,width:o.size,position:"absolute",top:0,opacity:o.opacity,display:o.alwaysVisible?"block":"none","border-radius":o.borderRadius,BorderRadius:o.borderRadius,MozBorderRadius:o.borderRadius,WebkitBorderRadius:o.borderRadius,zIndex:99}),M="right"==o.position?{right:o.distance}:{left:o.distance};R.css(M),D.css(M),x.wrap(E),x.parent().append(D),x.parent().append(R),o.railDraggable&&D.bind("mousedown",function(i){var s=e(document);return d=!0,t=parseFloat(D.css("top")),pageY=i.pageY,s.bind("mousemove.slimscroll",function(e){currTop=t+e.pageY-pageY,D.css("top",currTop),r(0,D.position().top,!1)}),s.bind("mouseup.slimscroll",function(e){d=!1,c(),s.unbind(".slimscroll")}),!1}).bind("selectstart.slimscroll",function(e){return e.stopPropagation(),e.preventDefault(),!1}),R.hover(function(){n()},function(){c()}),D.hover(function(){u=!0},function(){u=!1}),x.hover(function(){h=!0,n(),c()},function(){h=!1,c()}),x.bind("touchstart",function(e,t){e.originalEvent.touches.length&&(g=e.originalEvent.touches[0].pageY)}),x.bind("touchmove",function(e){if(y||e.originalEvent.preventDefault(),e.originalEvent.touches.length){var t=(g-e.originalEvent.touches[0].pageY)/o.touchScrollStep;r(t,!0),g=e.originalEvent.touches[0].pageY}}),l(),"bottom"===o.start?(D.css({top:x.outerHeight()-D.outerHeight()}),r(0,!0)):"top"!==o.start&&(r(e(o.start).position().top,null,!0),o.alwaysVisible||D.hide()),a(this)}}),this}}),e.fn.extend({slimscroll:e.fn.slimScroll})}(jQuery);
507
-
508
- /* qTip2 v3.0.3 | http://qtip2.com | Released under the MIT and GPL licenses. */
509
- !function(a,b,c){!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):jQuery&&!jQuery.fn.qtip&&a(jQuery)}(function(d){"use strict";function e(a,b,c,e){this.id=c,this.target=a,this.tooltip=D,this.elements={target:a},this._id=Q+"-"+c,this.timers={img:{}},this.options=b,this.plugins={},this.cache={event:{},target:d(),disabled:C,attr:e,onTooltip:C,lastClass:""},this.rendered=this.destroyed=this.disabled=this.waiting=this.hiddenDuringWait=this.positioning=this.triggering=C}function f(a){return a===D||"object"!==d.type(a)}function g(a){return!(d.isFunction(a)||a&&a.attr||a.length||"object"===d.type(a)&&(a.jquery||a.then))}function h(a){var b,c,e,h;return f(a)?C:(f(a.metadata)&&(a.metadata={type:a.metadata}),"content"in a&&(b=a.content,f(b)||b.jquery||b.done?(c=g(b)?C:b,b=a.content={text:c}):c=b.text,"ajax"in b&&(e=b.ajax,h=e&&e.once!==C,delete b.ajax,b.text=function(a,b){var f=c||d(this).attr(b.options.content.attr)||"Loading...",g=d.ajax(d.extend({},e,{context:b})).then(e.success,D,e.error).then(function(a){return a&&h&&b.set("content.text",a),a},function(a,c,d){b.destroyed||0===a.status||b.set("content.text",c+": "+d)});return h?f:(b.set("content.text",f),g)}),"title"in b&&(d.isPlainObject(b.title)&&(b.button=b.title.button,b.title=b.title.text),g(b.title||C)&&(b.title=C))),"position"in a&&f(a.position)&&(a.position={my:a.position,at:a.position}),"show"in a&&f(a.show)&&(a.show=a.show.jquery?{target:a.show}:a.show===B?{ready:B}:{event:a.show}),"hide"in a&&f(a.hide)&&(a.hide=a.hide.jquery?{target:a.hide}:{event:a.hide}),"style"in a&&f(a.style)&&(a.style={classes:a.style}),d.each(P,function(){this.sanitize&&this.sanitize(a)}),a)}function i(a,b){for(var c,d=0,e=a,f=b.split(".");e=e[f[d++]];)d<f.length&&(c=e);return[c||a,f.pop()]}function j(a,b){var c,d,e;for(c in this.checks)if(this.checks.hasOwnProperty(c))for(d in this.checks[c])this.checks[c].hasOwnProperty(d)&&(e=new RegExp(d,"i").exec(a))&&(b.push(e),("builtin"===c||this.plugins[c])&&this.checks[c][d].apply(this.plugins[c]||this,b))}function k(a){return T.concat("").join(a?"-"+a+" ":" ")}function l(a,b){return b>0?setTimeout(d.proxy(a,this),b):void a.call(this)}function m(a){this.tooltip.hasClass($)||(clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this.timers.show=l.call(this,function(){this.toggle(B,a)},this.options.show.delay))}function n(a){if(!this.tooltip.hasClass($)&&!this.destroyed){var b=d(a.relatedTarget),c=b.closest(U)[0]===this.tooltip[0],e=b[0]===this.options.show.target[0];if(clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this!==b[0]&&"mouse"===this.options.position.target&&c||this.options.hide.fixed&&/mouse(out|leave|move)/.test(a.type)&&(c||e))try{a.preventDefault(),a.stopImmediatePropagation()}catch(f){}else this.timers.hide=l.call(this,function(){this.toggle(C,a)},this.options.hide.delay,this)}}function o(a){!this.tooltip.hasClass($)&&this.options.hide.inactive&&(clearTimeout(this.timers.inactive),this.timers.inactive=l.call(this,function(){this.hide(a)},this.options.hide.inactive))}function p(a){this.rendered&&this.tooltip[0].offsetWidth>0&&this.reposition(a)}function q(a,c,e){d(b.body).delegate(a,(c.split?c:c.join("."+Q+" "))+"."+Q,function(){var a=w.api[d.attr(this,S)];a&&!a.disabled&&e.apply(a,arguments)})}function r(a,c,f){var g,i,j,k,l,m=d(b.body),n=a[0]===b?m:a,o=a.metadata?a.metadata(f.metadata):D,p="html5"===f.metadata.type&&o?o[f.metadata.name]:D,q=a.data(f.metadata.name||"qtipopts");try{q="string"==typeof q?d.parseJSON(q):q}catch(r){}if(k=d.extend(B,{},w.defaults,f,"object"==typeof q?h(q):D,h(p||o)),i=k.position,k.id=c,"boolean"==typeof k.content.text){if(j=a.attr(k.content.attr),k.content.attr===C||!j)return C;k.content.text=j}if(i.container.length||(i.container=m),i.target===C&&(i.target=n),k.show.target===C&&(k.show.target=n),k.show.solo===B&&(k.show.solo=i.container.closest("body")),k.hide.target===C&&(k.hide.target=n),k.position.viewport===B&&(k.position.viewport=i.container),i.container=i.container.eq(0),i.at=new y(i.at,B),i.my=new y(i.my),a.data(Q))if(k.overwrite)a.qtip("destroy",!0);else if(k.overwrite===C)return C;return a.attr(R,c),k.suppress&&(l=a.attr("title"))&&a.removeAttr("title").attr(aa,l).attr("title",""),g=new e(a,k,c,!!j),a.data(Q,g),g}function s(a){return a.charAt(0).toUpperCase()+a.slice(1)}function t(a,b){var d,e,f=b.charAt(0).toUpperCase()+b.slice(1),g=(b+" "+ta.join(f+" ")+f).split(" "),h=0;if(sa[b])return a.css(sa[b]);for(;d=g[h++];)if((e=a.css(d))!==c)return sa[b]=d,e}function u(a,b){return Math.ceil(parseFloat(t(a,b)))}function v(a,b){this._ns="tip",this.options=b,this.offset=b.offset,this.size=[b.width,b.height],this.qtip=a,this.init(a)}var w,x,y,z,A,B=!0,C=!1,D=null,E="x",F="y",G="width",H="height",I="top",J="left",K="bottom",L="right",M="center",N="flipinvert",O="shift",P={},Q="qtip",R="data-hasqtip",S="data-qtip-id",T=["ui-widget","ui-tooltip"],U="."+Q,V="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),W=Q+"-fixed",X=Q+"-default",Y=Q+"-focus",Z=Q+"-hover",$=Q+"-disabled",_="_replacedByqTip",aa="oldtitle",ba={ie:function(){var a,c;for(a=4,c=b.createElement("div");(c.innerHTML="<!--[if gt IE "+a+"]><i></i><![endif]-->")&&c.getElementsByTagName("i")[0];a+=1);return a>4?a:NaN}(),iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||C};x=e.prototype,x._when=function(a){return d.when.apply(d,a)},x.render=function(a){if(this.rendered||this.destroyed)return this;var b=this,c=this.options,e=this.cache,f=this.elements,g=c.content.text,h=c.content.title,i=c.content.button,j=c.position,k=[];return d.attr(this.target[0],"aria-describedby",this._id),e.posClass=this._createPosClass((this.position={my:j.my,at:j.at}).my),this.tooltip=f.tooltip=d("<div/>",{id:this._id,"class":[Q,X,c.style.classes,e.posClass].join(" "),width:c.style.width||"",height:c.style.height||"",tracking:"mouse"===j.target&&j.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":C,"aria-describedby":this._id+"-content","aria-hidden":B}).toggleClass($,this.disabled).attr(S,this.id).data(Q,this).appendTo(j.container).append(f.content=d("<div />",{"class":Q+"-content",id:this._id+"-content","aria-atomic":B})),this.rendered=-1,this.positioning=B,h&&(this._createTitle(),d.isFunction(h)||k.push(this._updateTitle(h,C))),i&&this._createButton(),d.isFunction(g)||k.push(this._updateContent(g,C)),this.rendered=B,this._setWidget(),d.each(P,function(a){var c;"render"===this.initialize&&(c=this(b))&&(b.plugins[a]=c)}),this._unassignEvents(),this._assignEvents(),this._when(k).then(function(){b._trigger("render"),b.positioning=C,b.hiddenDuringWait||!c.show.ready&&!a||b.toggle(B,e.event,C),b.hiddenDuringWait=C}),w.api[this.id]=this,this},x.destroy=function(a){function b(){if(!this.destroyed){this.destroyed=B;var a,b=this.target,c=b.attr(aa);this.rendered&&this.tooltip.stop(1,0).find("*").remove().end().remove(),d.each(this.plugins,function(){this.destroy&&this.destroy()});for(a in this.timers)this.timers.hasOwnProperty(a)&&clearTimeout(this.timers[a]);b.removeData(Q).removeAttr(S).removeAttr(R).removeAttr("aria-describedby"),this.options.suppress&&c&&b.attr("title",c).removeAttr(aa),this._unassignEvents(),this.options=this.elements=this.cache=this.timers=this.plugins=this.mouse=D,delete w.api[this.id]}}return this.destroyed?this.target:(a===B&&"hide"!==this.triggering||!this.rendered?b.call(this):(this.tooltip.one("tooltiphidden",d.proxy(b,this)),!this.triggering&&this.hide()),this.target)},z=x.checks={builtin:{"^id$":function(a,b,c,e){var f=c===B?w.nextid:c,g=Q+"-"+f;f!==C&&f.length>0&&!d("#"+g).length?(this._id=g,this.rendered&&(this.tooltip[0].id=this._id,this.elements.content[0].id=this._id+"-content",this.elements.title[0].id=this._id+"-title")):a[b]=e},"^prerender":function(a,b,c){c&&!this.rendered&&this.render(this.options.show.ready)},"^content.text$":function(a,b,c){this._updateContent(c)},"^content.attr$":function(a,b,c,d){this.options.content.text===this.target.attr(d)&&this._updateContent(this.target.attr(c))},"^content.title$":function(a,b,c){return c?(c&&!this.elements.title&&this._createTitle(),void this._updateTitle(c)):this._removeTitle()},"^content.button$":function(a,b,c){this._updateButton(c)},"^content.title.(text|button)$":function(a,b,c){this.set("content."+b,c)},"^position.(my|at)$":function(a,b,c){"string"==typeof c&&(this.position[b]=a[b]=new y(c,"at"===b))},"^position.container$":function(a,b,c){this.rendered&&this.tooltip.appendTo(c)},"^show.ready$":function(a,b,c){c&&(!this.rendered&&this.render(B)||this.toggle(B))},"^style.classes$":function(a,b,c,d){this.rendered&&this.tooltip.removeClass(d).addClass(c)},"^style.(width|height)":function(a,b,c){this.rendered&&this.tooltip.css(b,c)},"^style.widget|content.title":function(){this.rendered&&this._setWidget()},"^style.def":function(a,b,c){this.rendered&&this.tooltip.toggleClass(X,!!c)},"^events.(render|show|move|hide|focus|blur)$":function(a,b,c){this.rendered&&this.tooltip[(d.isFunction(c)?"":"un")+"bind"]("tooltip"+b,c)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){if(this.rendered){var a=this.options.position;this.tooltip.attr("tracking","mouse"===a.target&&a.adjust.mouse),this._unassignEvents(),this._assignEvents()}}}},x.get=function(a){if(this.destroyed)return this;var b=i(this.options,a.toLowerCase()),c=b[0][b[1]];return c.precedance?c.string():c};var ca=/^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i,da=/^prerender|show\.ready/i;x.set=function(a,b){if(this.destroyed)return this;var c,e=this.rendered,f=C,g=this.options;return"string"==typeof a?(c=a,a={},a[c]=b):a=d.extend({},a),d.each(a,function(b,c){if(e&&da.test(b))return void delete a[b];var h,j=i(g,b.toLowerCase());h=j[0][j[1]],j[0][j[1]]=c&&c.nodeType?d(c):c,f=ca.test(b)||f,a[b]=[j[0],j[1],c,h]}),h(g),this.positioning=B,d.each(a,d.proxy(j,this)),this.positioning=C,this.rendered&&this.tooltip[0].offsetWidth>0&&f&&this.reposition("mouse"===g.position.target?D:this.cache.event),this},x._update=function(a,b){var c=this,e=this.cache;return this.rendered&&a?(d.isFunction(a)&&(a=a.call(this.elements.target,e.event,this)||""),d.isFunction(a.then)?(e.waiting=B,a.then(function(a){return e.waiting=C,c._update(a,b)},D,function(a){return c._update(a,b)})):a===C||!a&&""!==a?C:(a.jquery&&a.length>0?b.empty().append(a.css({display:"block",visibility:"visible"})):b.html(a),this._waitForContent(b).then(function(a){c.rendered&&c.tooltip[0].offsetWidth>0&&c.reposition(e.event,!a.length)}))):C},x._waitForContent=function(a){var b=this.cache;return b.waiting=B,(d.fn.imagesLoaded?a.imagesLoaded():(new d.Deferred).resolve([])).done(function(){b.waiting=C}).promise()},x._updateContent=function(a,b){this._update(a,this.elements.content,b)},x._updateTitle=function(a,b){this._update(a,this.elements.title,b)===C&&this._removeTitle(C)},x._createTitle=function(){var a=this.elements,b=this._id+"-title";a.titlebar&&this._removeTitle(),a.titlebar=d("<div />",{"class":Q+"-titlebar "+(this.options.style.widget?k("header"):"")}).append(a.title=d("<div />",{id:b,"class":Q+"-title","aria-atomic":B})).insertBefore(a.content).delegate(".qtip-close","mousedown keydown mouseup keyup mouseout",function(a){d(this).toggleClass("ui-state-active ui-state-focus","down"===a.type.substr(-4))}).delegate(".qtip-close","mouseover mouseout",function(a){d(this).toggleClass("ui-state-hover","mouseover"===a.type)}),this.options.content.button&&this._createButton()},x._removeTitle=function(a){var b=this.elements;b.title&&(b.titlebar.remove(),b.titlebar=b.title=b.button=D,a!==C&&this.reposition())},x._createPosClass=function(a){return Q+"-pos-"+(a||this.options.position.my).abbrev()},x.reposition=function(c,e){if(!this.rendered||this.positioning||this.destroyed)return this;this.positioning=B;var f,g,h,i,j=this.cache,k=this.tooltip,l=this.options.position,m=l.target,n=l.my,o=l.at,p=l.viewport,q=l.container,r=l.adjust,s=r.method.split(" "),t=k.outerWidth(C),u=k.outerHeight(C),v=0,w=0,x=k.css("position"),y={left:0,top:0},z=k[0].offsetWidth>0,A=c&&"scroll"===c.type,D=d(a),E=q[0].ownerDocument,F=this.mouse;if(d.isArray(m)&&2===m.length)o={x:J,y:I},y={left:m[0],top:m[1]};else if("mouse"===m)o={x:J,y:I},(!r.mouse||this.options.hide.distance)&&j.origin&&j.origin.pageX?c=j.origin:!c||c&&("resize"===c.type||"scroll"===c.type)?c=j.event:F&&F.pageX&&(c=F),"static"!==x&&(y=q.offset()),E.body.offsetWidth!==(a.innerWidth||E.documentElement.clientWidth)&&(g=d(b.body).offset()),y={left:c.pageX-y.left+(g&&g.left||0),top:c.pageY-y.top+(g&&g.top||0)},r.mouse&&A&&F&&(y.left-=(F.scrollX||0)-D.scrollLeft(),y.top-=(F.scrollY||0)-D.scrollTop());else{if("event"===m?c&&c.target&&"scroll"!==c.type&&"resize"!==c.type?j.target=d(c.target):c.target||(j.target=this.elements.target):"event"!==m&&(j.target=d(m.jquery?m:this.elements.target)),m=j.target,m=d(m).eq(0),0===m.length)return this;m[0]===b||m[0]===a?(v=ba.iOS?a.innerWidth:m.width(),w=ba.iOS?a.innerHeight:m.height(),m[0]===a&&(y={top:(p||m).scrollTop(),left:(p||m).scrollLeft()})):P.imagemap&&m.is("area")?f=P.imagemap(this,m,o,P.viewport?s:C):P.svg&&m&&m[0].ownerSVGElement?f=P.svg(this,m,o,P.viewport?s:C):(v=m.outerWidth(C),w=m.outerHeight(C),y=m.offset()),f&&(v=f.width,w=f.height,g=f.offset,y=f.position),y=this.reposition.offset(m,y,q),(ba.iOS>3.1&&ba.iOS<4.1||ba.iOS>=4.3&&ba.iOS<4.33||!ba.iOS&&"fixed"===x)&&(y.left-=D.scrollLeft(),y.top-=D.scrollTop()),(!f||f&&f.adjustable!==C)&&(y.left+=o.x===L?v:o.x===M?v/2:0,y.top+=o.y===K?w:o.y===M?w/2:0)}return y.left+=r.x+(n.x===L?-t:n.x===M?-t/2:0),y.top+=r.y+(n.y===K?-u:n.y===M?-u/2:0),P.viewport?(h=y.adjusted=P.viewport(this,y,l,v,w,t,u),g&&h.left&&(y.left+=g.left),g&&h.top&&(y.top+=g.top),h.my&&(this.position.my=h.my)):y.adjusted={left:0,top:0},j.posClass!==(i=this._createPosClass(this.position.my))&&(j.posClass=i,k.removeClass(j.posClass).addClass(i)),this._trigger("move",[y,p.elem||p],c)?(delete y.adjusted,e===C||!z||isNaN(y.left)||isNaN(y.top)||"mouse"===m||!d.isFunction(l.effect)?k.css(y):d.isFunction(l.effect)&&(l.effect.call(k,this,d.extend({},y)),k.queue(function(a){d(this).css({opacity:"",height:""}),ba.ie&&this.style.removeAttribute("filter"),a()})),this.positioning=C,this):this},x.reposition.offset=function(a,c,e){function f(a,b){c.left+=b*a.scrollLeft(),c.top+=b*a.scrollTop()}if(!e[0])return c;var g,h,i,j,k=d(a[0].ownerDocument),l=!!ba.ie&&"CSS1Compat"!==b.compatMode,m=e[0];do"static"!==(h=d.css(m,"position"))&&("fixed"===h?(i=m.getBoundingClientRect(),f(k,-1)):(i=d(m).position(),i.left+=parseFloat(d.css(m,"borderLeftWidth"))||0,i.top+=parseFloat(d.css(m,"borderTopWidth"))||0),c.left-=i.left+(parseFloat(d.css(m,"marginLeft"))||0),c.top-=i.top+(parseFloat(d.css(m,"marginTop"))||0),g||"hidden"===(j=d.css(m,"overflow"))||"visible"===j||(g=d(m)));while(m=m.offsetParent);return g&&(g[0]!==k[0]||l)&&f(g,1),c};var ea=(y=x.reposition.Corner=function(a,b){a=(""+a).replace(/([A-Z])/," $1").replace(/middle/gi,M).toLowerCase(),this.x=(a.match(/left|right/i)||a.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(a.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase(),this.forceY=!!b;var c=a.charAt(0);this.precedance="t"===c||"b"===c?F:E}).prototype;ea.invert=function(a,b){this[a]=this[a]===J?L:this[a]===L?J:b||this[a]},ea.string=function(a){var b=this.x,c=this.y,d=b!==c?"center"===b||"center"!==c&&(this.precedance===F||this.forceY)?[c,b]:[b,c]:[b];return a!==!1?d.join(" "):d},ea.abbrev=function(){var a=this.string(!1);return a[0].charAt(0)+(a[1]&&a[1].charAt(0)||"")},ea.clone=function(){return new y(this.string(),this.forceY)},x.toggle=function(a,c){var e=this.cache,f=this.options,g=this.tooltip;if(c){if(/over|enter/.test(c.type)&&e.event&&/out|leave/.test(e.event.type)&&f.show.target.add(c.target).length===f.show.target.length&&g.has(c.relatedTarget).length)return this;e.event=d.event.fix(c)}if(this.waiting&&!a&&(this.hiddenDuringWait=B),!this.rendered)return a?this.render(1):this;if(this.destroyed||this.disabled)return this;var h,i,j,k=a?"show":"hide",l=this.options[k],m=this.options.position,n=this.options.content,o=this.tooltip.css("width"),p=this.tooltip.is(":visible"),q=a||1===l.target.length,r=!c||l.target.length<2||e.target[0]===c.target;return(typeof a).search("boolean|number")&&(a=!p),h=!g.is(":animated")&&p===a&&r,i=h?D:!!this._trigger(k,[90]),this.destroyed?this:(i!==C&&a&&this.focus(c),!i||h?this:(d.attr(g[0],"aria-hidden",!a),a?(this.mouse&&(e.origin=d.event.fix(this.mouse)),d.isFunction(n.text)&&this._updateContent(n.text,C),d.isFunction(n.title)&&this._updateTitle(n.title,C),!A&&"mouse"===m.target&&m.adjust.mouse&&(d(b).bind("mousemove."+Q,this._storeMouse),A=B),o||g.css("width",g.outerWidth(C)),this.reposition(c,arguments[2]),o||g.css("width",""),l.solo&&("string"==typeof l.solo?d(l.solo):d(U,l.solo)).not(g).not(l.target).qtip("hide",new d.Event("tooltipsolo"))):(clearTimeout(this.timers.show),delete e.origin,A&&!d(U+'[tracking="true"]:visible',l.solo).not(g).length&&(d(b).unbind("mousemove."+Q),A=C),this.blur(c)),j=d.proxy(function(){a?(ba.ie&&g[0].style.removeAttribute("filter"),g.css("overflow",""),"string"==typeof l.autofocus&&d(this.options.show.autofocus,g).focus(),this.options.show.target.trigger("qtip-"+this.id+"-inactive")):g.css({display:"",visibility:"",opacity:"",left:"",top:""}),this._trigger(a?"visible":"hidden")},this),l.effect===C||q===C?(g[k](),j()):d.isFunction(l.effect)?(g.stop(1,1),l.effect.call(g,this),g.queue("fx",function(a){j(),a()})):g.fadeTo(90,a?1:0,j),a&&l.target.trigger("qtip-"+this.id+"-inactive"),this))},x.show=function(a){return this.toggle(B,a)},x.hide=function(a){return this.toggle(C,a)},x.focus=function(a){if(!this.rendered||this.destroyed)return this;var b=d(U),c=this.tooltip,e=parseInt(c[0].style.zIndex,10),f=w.zindex+b.length;return c.hasClass(Y)||this._trigger("focus",[f],a)&&(e!==f&&(b.each(function(){this.style.zIndex>e&&(this.style.zIndex=this.style.zIndex-1)}),b.filter("."+Y).qtip("blur",a)),c.addClass(Y)[0].style.zIndex=f),this},x.blur=function(a){return!this.rendered||this.destroyed?this:(this.tooltip.removeClass(Y),this._trigger("blur",[this.tooltip.css("zIndex")],a),this)},x.disable=function(a){return this.destroyed?this:("toggle"===a?a=!(this.rendered?this.tooltip.hasClass($):this.disabled):"boolean"!=typeof a&&(a=B),this.rendered&&this.tooltip.toggleClass($,a).attr("aria-disabled",a),this.disabled=!!a,this)},x.enable=function(){return this.disable(C)},x._createButton=function(){var a=this,b=this.elements,c=b.tooltip,e=this.options.content.button,f="string"==typeof e,g=f?e:"Close tooltip";b.button&&b.button.remove(),e.jquery?b.button=e:b.button=d("<a />",{"class":"qtip-close "+(this.options.style.widget?"":Q+"-icon"),title:g,"aria-label":g}).prepend(d("<span />",{"class":"ui-icon ui-icon-close",html:"&times;"})),b.button.appendTo(b.titlebar||c).attr("role","button").click(function(b){return c.hasClass($)||a.hide(b),C})},x._updateButton=function(a){if(!this.rendered)return C;var b=this.elements.button;a?this._createButton():b.remove()},x._setWidget=function(){var a=this.options.style.widget,b=this.elements,c=b.tooltip,d=c.hasClass($);c.removeClass($),$=a?"ui-state-disabled":"qtip-disabled",c.toggleClass($,d),c.toggleClass("ui-helper-reset "+k(),a).toggleClass(X,this.options.style.def&&!a),b.content&&b.content.toggleClass(k("content"),a),b.titlebar&&b.titlebar.toggleClass(k("header"),a),b.button&&b.button.toggleClass(Q+"-icon",!a)},x._storeMouse=function(a){return(this.mouse=d.event.fix(a)).type="mousemove",this},x._bind=function(a,b,c,e,f){if(a&&c&&b.length){var g="."+this._id+(e?"-"+e:"");return d(a).bind((b.split?b:b.join(g+" "))+g,d.proxy(c,f||this)),this}},x._unbind=function(a,b){return a&&d(a).unbind("."+this._id+(b?"-"+b:"")),this},x._trigger=function(a,b,c){var e=new d.Event("tooltip"+a);return e.originalEvent=c&&d.extend({},c)||this.cache.event||D,this.triggering=a,this.tooltip.trigger(e,[this].concat(b||[])),this.triggering=C,!e.isDefaultPrevented()},x._bindEvents=function(a,b,c,e,f,g){var h=c.filter(e).add(e.filter(c)),i=[];h.length&&(d.each(b,function(b,c){var e=d.inArray(c,a);e>-1&&i.push(a.splice(e,1)[0])}),i.length&&(this._bind(h,i,function(a){var b=this.rendered?this.tooltip[0].offsetWidth>0:!1;(b?g:f).call(this,a)}),c=c.not(h),e=e.not(h))),this._bind(c,a,f),this._bind(e,b,g)},x._assignInitialEvents=function(a){function b(a){return this.disabled||this.destroyed?C:(this.cache.event=a&&d.event.fix(a),this.cache.target=a&&d(a.target),clearTimeout(this.timers.show),void(this.timers.show=l.call(this,function(){this.render("object"==typeof a||c.show.ready)},c.prerender?0:c.show.delay)))}var c=this.options,e=c.show.target,f=c.hide.target,g=c.show.event?d.trim(""+c.show.event).split(" "):[],h=c.hide.event?d.trim(""+c.hide.event).split(" "):[];this._bind(this.elements.target,["remove","removeqtip"],function(){this.destroy(!0)},"destroy"),/mouse(over|enter)/i.test(c.show.event)&&!/mouse(out|leave)/i.test(c.hide.event)&&h.push("mouseleave"),this._bind(e,"mousemove",function(a){this._storeMouse(a),this.cache.onTarget=B}),this._bindEvents(g,h,e,f,b,function(){return this.timers?void clearTimeout(this.timers.show):C}),(c.show.ready||c.prerender)&&b.call(this,a)},x._assignEvents=function(){var c=this,e=this.options,f=e.position,g=this.tooltip,h=e.show.target,i=e.hide.target,j=f.container,k=f.viewport,l=d(b),q=d(a),r=e.show.event?d.trim(""+e.show.event).split(" "):[],s=e.hide.event?d.trim(""+e.hide.event).split(" "):[];d.each(e.events,function(a,b){c._bind(g,"toggle"===a?["tooltipshow","tooltiphide"]:["tooltip"+a],b,null,g)}),/mouse(out|leave)/i.test(e.hide.event)&&"window"===e.hide.leave&&this._bind(l,["mouseout","blur"],function(a){/select|option/.test(a.target.nodeName)||a.relatedTarget||this.hide(a)}),e.hide.fixed?i=i.add(g.addClass(W)):/mouse(over|enter)/i.test(e.show.event)&&this._bind(i,"mouseleave",function(){clearTimeout(this.timers.show)}),(""+e.hide.event).indexOf("unfocus")>-1&&this._bind(j.closest("html"),["mousedown","touchstart"],function(a){var b=d(a.target),c=this.rendered&&!this.tooltip.hasClass($)&&this.tooltip[0].offsetWidth>0,e=b.parents(U).filter(this.tooltip[0]).length>0;b[0]===this.target[0]||b[0]===this.tooltip[0]||e||this.target.has(b[0]).length||!c||this.hide(a)}),"number"==typeof e.hide.inactive&&(this._bind(h,"qtip-"+this.id+"-inactive",o,"inactive"),this._bind(i.add(g),w.inactiveEvents,o)),this._bindEvents(r,s,h,i,m,n),this._bind(h.add(g),"mousemove",function(a){if("number"==typeof e.hide.distance){var b=this.cache.origin||{},c=this.options.hide.distance,d=Math.abs;(d(a.pageX-b.pageX)>=c||d(a.pageY-b.pageY)>=c)&&this.hide(a)}this._storeMouse(a)}),"mouse"===f.target&&f.adjust.mouse&&(e.hide.event&&this._bind(h,["mouseenter","mouseleave"],function(a){return this.cache?void(this.cache.onTarget="mouseenter"===a.type):C}),this._bind(l,"mousemove",function(a){this.rendered&&this.cache.onTarget&&!this.tooltip.hasClass($)&&this.tooltip[0].offsetWidth>0&&this.reposition(a)})),(f.adjust.resize||k.length)&&this._bind(d.event.special.resize?k:q,"resize",p),f.adjust.scroll&&this._bind(q.add(f.container),"scroll",p)},x._unassignEvents=function(){var c=this.options,e=c.show.target,f=c.hide.target,g=d.grep([this.elements.target[0],this.rendered&&this.tooltip[0],c.position.container[0],c.position.viewport[0],c.position.container.closest("html")[0],a,b],function(a){return"object"==typeof a});e&&e.toArray&&(g=g.concat(e.toArray())),f&&f.toArray&&(g=g.concat(f.toArray())),this._unbind(g)._unbind(g,"destroy")._unbind(g,"inactive")},d(function(){q(U,["mouseenter","mouseleave"],function(a){var b="mouseenter"===a.type,c=d(a.currentTarget),e=d(a.relatedTarget||a.target),f=this.options;b?(this.focus(a),c.hasClass(W)&&!c.hasClass($)&&clearTimeout(this.timers.hide)):"mouse"===f.position.target&&f.position.adjust.mouse&&f.hide.event&&f.show.target&&!e.closest(f.show.target[0]).length&&this.hide(a),c.toggleClass(Z,b)}),q("["+S+"]",V,o)}),w=d.fn.qtip=function(a,b,e){var f=(""+a).toLowerCase(),g=D,i=d.makeArray(arguments).slice(1),j=i[i.length-1],k=this[0]?d.data(this[0],Q):D;return!arguments.length&&k||"api"===f?k:"string"==typeof a?(this.each(function(){var a=d.data(this,Q);if(!a)return B;if(j&&j.timeStamp&&(a.cache.event=j),!b||"option"!==f&&"options"!==f)a[f]&&a[f].apply(a,i);else{if(e===c&&!d.isPlainObject(b))return g=a.get(b),C;a.set(b,e)}}),g!==D?g:this):"object"!=typeof a&&arguments.length?void 0:(k=h(d.extend(B,{},a)),this.each(function(a){var b,c;return c=d.isArray(k.id)?k.id[a]:k.id,c=!c||c===C||c.length<1||w.api[c]?w.nextid++:c,b=r(d(this),c,k),b===C?B:(w.api[c]=b,d.each(P,function(){"initialize"===this.initialize&&this(b)}),void b._assignInitialEvents(j))}))},d.qtip=e,w.api={},d.each({attr:function(a,b){if(this.length){var c=this[0],e="title",f=d.data(c,"qtip");if(a===e&&f&&f.options&&"object"==typeof f&&"object"==typeof f.options&&f.options.suppress)return arguments.length<2?d.attr(c,aa):(f&&f.options.content.attr===e&&f.cache.attr&&f.set("content.text",b),this.attr(aa,b))}return d.fn["attr"+_].apply(this,arguments)},clone:function(a){var b=d.fn["clone"+_].apply(this,arguments);return a||b.filter("["+aa+"]").attr("title",function(){return d.attr(this,aa)}).removeAttr(aa),b}},function(a,b){if(!b||d.fn[a+_])return B;var c=d.fn[a+_]=d.fn[a];d.fn[a]=function(){return b.apply(this,arguments)||c.apply(this,arguments)}}),d.ui||(d["cleanData"+_]=d.cleanData,d.cleanData=function(a){for(var b,c=0;(b=d(a[c])).length;c++)if(b.attr(R))try{b.triggerHandler("removeqtip")}catch(e){}d["cleanData"+_].apply(this,arguments)}),w.version="3.0.3",w.nextid=0,w.inactiveEvents=V,w.zindex=15e3,w.defaults={prerender:C,id:C,overwrite:B,suppress:B,content:{text:B,attr:"title",title:C,button:C},position:{my:"top left",at:"bottom right",target:C,container:C,viewport:C,adjust:{x:0,y:0,mouse:B,scroll:B,resize:B,method:"flipinvert flipinvert"},effect:function(a,b){d(this).animate(b,{duration:200,queue:C})}},show:{target:C,event:"mouseenter",effect:B,delay:90,solo:C,ready:C,autofocus:C},hide:{target:C,event:"mouseleave",effect:B,delay:0,fixed:C,inactive:C,leave:"window",distance:C},style:{classes:"",widget:C,width:C,height:C,def:B},events:{render:D,move:D,show:D,hide:D,toggle:D,visible:D,hidden:D,focus:D,blur:D}};var fa,ga,ha,ia,ja,ka="margin",la="border",ma="color",na="background-color",oa="transparent",pa=" !important",qa=!!b.createElement("canvas").getContext,ra=/rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,sa={},ta=["Webkit","O","Moz","ms"];qa?(ia=a.devicePixelRatio||1,ja=function(){var a=b.createElement("canvas").getContext("2d");return a.backingStorePixelRatio||a.webkitBackingStorePixelRatio||a.mozBackingStorePixelRatio||a.msBackingStorePixelRatio||a.oBackingStorePixelRatio||1}(),ha=ia/ja):ga=function(a,b,c){return"<qtipvml:"+a+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(b||"")+' style="behavior: url(#default#VML); '+(c||"")+'" />'},d.extend(v.prototype,{init:function(a){var b,c;c=this.element=a.elements.tip=d("<div />",{"class":Q+"-tip"}).prependTo(a.tooltip),qa?(b=d("<canvas />").appendTo(this.element)[0].getContext("2d"),b.lineJoin="miter",b.miterLimit=1e5,b.save()):(b=ga("shape",'coordorigin="0,0"',"position:absolute;"),this.element.html(b+b),a._bind(d("*",c).add(c),["click","mousedown"],function(a){a.stopPropagation()},this._ns)),a._bind(a.tooltip,"tooltipmove",this.reposition,this._ns,this),this.create()},_swapDimensions:function(){this.size[0]=this.options.height,this.size[1]=this.options.width},_resetDimensions:function(){this.size[0]=this.options.width,this.size[1]=this.options.height},_useTitle:function(a){var b=this.qtip.elements.titlebar;return b&&(a.y===I||a.y===M&&this.element.position().top+this.size[1]/2+this.options.offset<b.outerHeight(B))},_parseCorner:function(a){var b=this.qtip.options.position.my;return a===C||b===C?a=C:a===B?a=new y(b.string()):a.string||(a=new y(a),a.fixed=B),a},_parseWidth:function(a,b,c){var d=this.qtip.elements,e=la+s(b)+"Width";return(c?u(c,e):u(d.content,e)||u(this._useTitle(a)&&d.titlebar||d.content,e)||u(d.tooltip,e))||0},_parseRadius:function(a){var b=this.qtip.elements,c=la+s(a.y)+s(a.x)+"Radius";return ba.ie<9?0:u(this._useTitle(a)&&b.titlebar||b.content,c)||u(b.tooltip,c)||0},_invalidColour:function(a,b,c){var d=a.css(b);return!d||c&&d===a.css(c)||ra.test(d)?C:d},_parseColours:function(a){var b=this.qtip.elements,c=this.element.css("cssText",""),e=la+s(a[a.precedance])+s(ma),f=this._useTitle(a)&&b.titlebar||b.content,g=this._invalidColour,h=[];return h[0]=g(c,na)||g(f,na)||g(b.content,na)||g(b.tooltip,na)||c.css(na),h[1]=g(c,e,ma)||g(f,e,ma)||g(b.content,e,ma)||g(b.tooltip,e,ma)||b.tooltip.css(e),d("*",c).add(c).css("cssText",na+":"+oa+pa+";"+la+":0"+pa+";"),h},_calculateSize:function(a){var b,c,d,e=a.precedance===F,f=this.options.width,g=this.options.height,h="c"===a.abbrev(),i=(e?f:g)*(h?.5:1),j=Math.pow,k=Math.round,l=Math.sqrt(j(i,2)+j(g,2)),m=[this.border/i*l,this.border/g*l];return m[2]=Math.sqrt(j(m[0],2)-j(this.border,2)),m[3]=Math.sqrt(j(m[1],2)-j(this.border,2)),b=l+m[2]+m[3]+(h?0:m[0]),c=b/l,d=[k(c*f),k(c*g)],e?d:d.reverse()},_calculateTip:function(a,b,c){c=c||1,b=b||this.size;var d=b[0]*c,e=b[1]*c,f=Math.ceil(d/2),g=Math.ceil(e/2),h={br:[0,0,d,e,d,0],bl:[0,0,d,0,0,e],tr:[0,e,d,0,d,e],tl:[0,0,0,e,d,e],tc:[0,e,f,0,d,e],bc:[0,0,d,0,f,e],rc:[0,0,d,g,0,e],lc:[d,0,d,e,0,g]};return h.lt=h.br,h.rt=h.bl,h.lb=h.tr,h.rb=h.tl,h[a.abbrev()]},_drawCoords:function(a,b){a.beginPath(),a.moveTo(b[0],b[1]),a.lineTo(b[2],b[3]),a.lineTo(b[4],b[5]),a.closePath()},create:function(){var a=this.corner=(qa||ba.ie)&&this._parseCorner(this.options.corner);return this.enabled=!!this.corner&&"c"!==this.corner.abbrev(),this.enabled&&(this.qtip.cache.corner=a.clone(),this.update()),this.element.toggle(this.enabled),this.corner},update:function(b,c){if(!this.enabled)return this;var e,f,g,h,i,j,k,l,m=this.qtip.elements,n=this.element,o=n.children(),p=this.options,q=this.size,r=p.mimic,s=Math.round;b||(b=this.qtip.cache.corner||this.corner),r===C?r=b:(r=new y(r),r.precedance=b.precedance,"inherit"===r.x?r.x=b.x:"inherit"===r.y?r.y=b.y:r.x===r.y&&(r[b.precedance]=b[b.precedance])),f=r.precedance,b.precedance===E?this._swapDimensions():this._resetDimensions(),e=this.color=this._parseColours(b),e[1]!==oa?(l=this.border=this._parseWidth(b,b[b.precedance]),p.border&&1>l&&!ra.test(e[1])&&(e[0]=e[1]),this.border=l=p.border!==B?p.border:l):this.border=l=0,k=this.size=this._calculateSize(b),n.css({width:k[0],height:k[1],lineHeight:k[1]+"px"}),j=b.precedance===F?[s(r.x===J?l:r.x===L?k[0]-q[0]-l:(k[0]-q[0])/2),s(r.y===I?k[1]-q[1]:0)]:[s(r.x===J?k[0]-q[0]:0),s(r.y===I?l:r.y===K?k[1]-q[1]-l:(k[1]-q[1])/2)],qa?(g=o[0].getContext("2d"),g.restore(),g.save(),g.clearRect(0,0,6e3,6e3),h=this._calculateTip(r,q,ha),i=this._calculateTip(r,this.size,ha),o.attr(G,k[0]*ha).attr(H,k[1]*ha),o.css(G,k[0]).css(H,k[1]),this._drawCoords(g,i),g.fillStyle=e[1],g.fill(),g.translate(j[0]*ha,j[1]*ha),this._drawCoords(g,h),g.fillStyle=e[0],g.fill()):(h=this._calculateTip(r),h="m"+h[0]+","+h[1]+" l"+h[2]+","+h[3]+" "+h[4]+","+h[5]+" xe",j[2]=l&&/^(r|b)/i.test(b.string())?8===ba.ie?2:1:0,o.css({coordsize:k[0]+l+" "+k[1]+l,antialias:""+(r.string().indexOf(M)>-1),left:j[0]-j[2]*Number(f===E),top:j[1]-j[2]*Number(f===F),width:k[0]+l,height:k[1]+l}).each(function(a){var b=d(this);b[b.prop?"prop":"attr"]({coordsize:k[0]+l+" "+k[1]+l,path:h,fillcolor:e[0],filled:!!a,stroked:!a}).toggle(!(!l&&!a)),!a&&b.html(ga("stroke",'weight="'+2*l+'px" color="'+e[1]+'" miterlimit="1000" joinstyle="miter"'))})),a.opera&&setTimeout(function(){m.tip.css({display:"inline-block",visibility:"visible"})},1),c!==C&&this.calculate(b,k)},calculate:function(a,b){if(!this.enabled)return C;var c,e,f=this,g=this.qtip.elements,h=this.element,i=this.options.offset,j={};return a=a||this.corner,c=a.precedance,b=b||this._calculateSize(a),e=[a.x,a.y],c===E&&e.reverse(),d.each(e,function(d,e){var h,k,l;e===M?(h=c===F?J:I,j[h]="50%",j[ka+"-"+h]=-Math.round(b[c===F?0:1]/2)+i):(h=f._parseWidth(a,e,g.tooltip),k=f._parseWidth(a,e,g.content),l=f._parseRadius(a),j[e]=Math.max(-f.border,d?k:i+(l>h?l:-h)))}),j[a[c]]-=b[c===E?0:1],h.css({margin:"",top:"",bottom:"",left:"",right:""}).css(j),j},reposition:function(a,b,d){function e(a,b,c,d,e){a===O&&j.precedance===b&&k[d]&&j[c]!==M?j.precedance=j.precedance===E?F:E:a!==O&&k[d]&&(j[b]=j[b]===M?k[d]>0?d:e:j[b]===d?e:d)}function f(a,b,e){j[a]===M?p[ka+"-"+b]=o[a]=g[ka+"-"+b]-k[b]:(h=g[e]!==c?[k[b],-g[b]]:[-k[b],g[b]],(o[a]=Math.max(h[0],h[1]))>h[0]&&(d[b]-=k[b],o[b]=C),p[g[e]!==c?e:b]=o[a])}if(this.enabled){var g,h,i=b.cache,j=this.corner.clone(),k=d.adjusted,l=b.options.position.adjust.method.split(" "),m=l[0],n=l[1]||l[0],o={left:C,top:C,x:0,y:0},p={};this.corner.fixed!==B&&(e(m,E,F,J,L),e(n,F,E,I,K),j.string()===i.corner.string()&&i.cornerTop===k.top&&i.cornerLeft===k.left||this.update(j,C)),g=this.calculate(j),g.right!==c&&(g.left=-g.right),g.bottom!==c&&(g.top=-g.bottom),g.user=this.offset,o.left=m===O&&!!k.left,o.left&&f(E,J,L),o.top=n===O&&!!k.top,o.top&&f(F,I,K),this.element.css(p).toggle(!(o.x&&o.y||j.x===M&&o.y||j.y===M&&o.x)),d.left-=g.left.charAt?g.user:m!==O||o.top||!o.left&&!o.top?g.left+this.border:0,d.top-=g.top.charAt?g.user:n!==O||o.left||!o.left&&!o.top?g.top+this.border:0,i.cornerLeft=k.left,i.cornerTop=k.top,i.corner=j.clone()}},destroy:function(){this.qtip._unbind(this.qtip.tooltip,this._ns),this.qtip.elements.tip&&this.qtip.elements.tip.find("*").remove().end().remove()}}),fa=P.tip=function(a){return new v(a,a.options.style.tip)},fa.initialize="render",fa.sanitize=function(a){if(a.style&&"tip"in a.style){var b=a.style.tip;"object"!=typeof b&&(b=a.style.tip={corner:b}),/string|boolean/i.test(typeof b.corner)||(b.corner=B)}},z.tip={"^position.my|style.tip.(corner|mimic|border)$":function(){this.create(),this.qtip.reposition()},"^style.tip.(height|width)$":function(a){this.size=[a.width,a.height],this.update(),this.qtip.reposition()},"^content.title|style.(classes|widget)$":function(){this.update()}},d.extend(B,w.defaults,{style:{tip:{corner:B,mimic:C,width:6,height:6,border:B,offset:0}}}),P.viewport=function(c,d,e,f,g,h,i){function j(a,b,c,e,f,g,h,i,j){var k=d[f],s=u[a],t=v[a],w=c===O,x=s===f?j:s===g?-j:-j/2,y=t===f?i:t===g?-i:-i/2,z=q[f]+r[f]-(n?0:m[f]),A=z-k,B=k+j-(h===G?o:p)-z,C=x-(u.precedance===a||s===u[b]?y:0)-(t===M?i/2:0);return w?(C=(s===f?1:-1)*x,d[f]+=A>0?A:B>0?-B:0,d[f]=Math.max(-m[f]+r[f],k-C,Math.min(Math.max(-m[f]+r[f]+(h===G?o:p),k+C),d[f],"center"===s?k-x:1e9))):(e*=c===N?2:0,A>0&&(s!==f||B>0)?(d[f]-=C+e,l.invert(a,f)):B>0&&(s!==g||A>0)&&(d[f]-=(s===M?-C:C)+e,l.invert(a,g)),d[f]<q[f]&&-d[f]>B&&(d[f]=k,l=u.clone())),d[f]-k}var k,l,m,n,o,p,q,r,s=e.target,t=c.elements.tooltip,u=e.my,v=e.at,w=e.adjust,x=w.method.split(" "),y=x[0],z=x[1]||x[0],A=e.viewport,B=e.container,D={left:0,top:0};return A.jquery&&s[0]!==a&&s[0]!==b.body&&"none"!==w.method?(m=B.offset()||D,n="static"===B.css("position"),k="fixed"===t.css("position"),o=A[0]===a?A.width():A.outerWidth(C),p=A[0]===a?A.height():A.outerHeight(C),q={left:k?0:A.scrollLeft(),top:k?0:A.scrollTop()},r=A.offset()||D,"shift"===y&&"shift"===z||(l=u.clone()),D={left:"none"!==y?j(E,F,y,w.x,J,L,G,f,h):0,top:"none"!==z?j(F,E,z,w.y,I,K,H,g,i):0,my:l}):D}})}(window,document);
510
-
511
- /* bootstrap-switch v3.3.4 | http://bootstrapswitch.com/ | Copyright (c) 2012-2017 Mattia Larentis & Emanuele Marchi. Released under the Apache 2.0 license. */
512
- (function(a,b){if('function'==typeof define&&define.amd)define(['jquery'],b);else if('undefined'!=typeof exports)b(require('jquery'));else{b(a.jquery),a.bootstrapSwitch={exports:{}}.exports}})(this,function(a){'use strict';function c(j,k){if(!(j instanceof k))throw new TypeError('Cannot call a class as a function')}var d=function(j){return j&&j.__esModule?j:{default:j}}(a),e=Object.assign||function(j){for(var l,k=1;k<arguments.length;k++)for(var m in l=arguments[k],l)Object.prototype.hasOwnProperty.call(l,m)&&(j[m]=l[m]);return j},f=function(){function j(k,l){for(var n,m=0;m<l.length;m++)n=l[m],n.enumerable=n.enumerable||!1,n.configurable=!0,'value'in n&&(n.writable=!0),Object.defineProperty(k,n.key,n)}return function(k,l,m){return l&&j(k.prototype,l),m&&j(k,m),k}}(),g=d.default||window.jQuery||window.$,h=function(){function j(k){var l=this,m=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};c(this,j),this.$element=g(k),this.options=g.extend({},g.fn.bootstrapSwitch.defaults,this._getElementOptions(),m),this.prevOptions={},this.$wrapper=g('<div>',{class:function(){var o=[];return o.push(l.options.state?'on':'off'),l.options.size&&o.push(l.options.size),l.options.disabled&&o.push('disabled'),l.options.readonly&&o.push('readonly'),l.options.indeterminate&&o.push('indeterminate'),l.options.inverse&&o.push('inverse'),l.$element.attr('id')&&o.push('id-'+l.$element.attr('id')),o.map(l._getClass.bind(l)).concat([l.options.baseClass],l._getClasses(l.options.wrapperClass)).join(' ')}}),this.$container=g('<div>',{class:this._getClass('container')}),this.$on=g('<span>',{html:this.options.onText,class:this._getClass('handle-on')+' '+this._getClass(this.options.onColor)}),this.$off=g('<span>',{html:this.options.offText,class:this._getClass('handle-off')+' '+this._getClass(this.options.offColor)}),this.$label=g('<span>',{html:this.options.labelText,class:this._getClass('label')}),this.$element.on('init.bootstrapSwitch',this.options.onInit.bind(this,k)),this.$element.on('switchChange.bootstrapSwitch',function(){for(var n=arguments.length,o=Array(n),p=0;p<n;p++)o[p]=arguments[p];!1===l.options.onSwitchChange.apply(k,o)&&(l.$element.is(':radio')?g('[name="'+l.$element.attr('name')+'"]').trigger('previousState.bootstrapSwitch',!0):l.$element.trigger('previousState.bootstrapSwitch',!0))}),this.$container=this.$element.wrap(this.$container).parent(),this.$wrapper=this.$container.wrap(this.$wrapper).parent(),this.$element.before(this.options.inverse?this.$off:this.$on).before(this.$label).before(this.options.inverse?this.$on:this.$off),this.options.indeterminate&&this.$element.prop('indeterminate',!0),this._init(),this._elementHandlers(),this._handleHandlers(),this._labelHandlers(),this._formHandler(),this._externalLabelHandler(),this.$element.trigger('init.bootstrapSwitch',this.options.state)}return f(j,[{key:'setPrevOptions',value:function(){this.prevOptions=e({},this.options)}},{key:'state',value:function(l,m){return'undefined'==typeof l?this.options.state:this.options.disabled||this.options.readonly||this.options.state&&!this.options.radioAllOff&&this.$element.is(':radio')?this.$element:(this.$element.is(':radio')?g('[name="'+this.$element.attr('name')+'"]').trigger('setPreviousOptions.bootstrapSwitch'):this.$element.trigger('setPreviousOptions.bootstrapSwitch'),this.options.indeterminate&&this.indeterminate(!1),this.$element.prop('checked',!!l).trigger('change.bootstrapSwitch',m),this.$element)}},{key:'toggleState',value:function(l){return this.options.disabled||this.options.readonly?this.$element:this.options.indeterminate?(this.indeterminate(!1),this.state(!0)):this.$element.prop('checked',!this.options.state).trigger('change.bootstrapSwitch',l)}},{key:'size',value:function(l){return'undefined'==typeof l?this.options.size:(null!=this.options.size&&this.$wrapper.removeClass(this._getClass(this.options.size)),l&&this.$wrapper.addClass(this._getClass(l)),this._width(),this._containerPosition(),this.options.size=l,this.$element)}},{key:'animate',value:function(l){return'undefined'==typeof l?this.options.animate:this.options.animate===!!l?this.$element:this.toggleAnimate()}},{key:'toggleAnimate',value:function(){return this.options.animate=!this.options.animate,this.$wrapper.toggleClass(this._getClass('animate')),this.$element}},{key:'disabled',value:function(l){return'undefined'==typeof l?this.options.disabled:this.options.disabled===!!l?this.$element:this.toggleDisabled()}},{key:'toggleDisabled',value:function(){return this.options.disabled=!this.options.disabled,this.$element.prop('disabled',this.options.disabled),this.$wrapper.toggleClass(this._getClass('disabled')),this.$element}},{key:'readonly',value:function(l){return'undefined'==typeof l?this.options.readonly:this.options.readonly===!!l?this.$element:this.toggleReadonly()}},{key:'toggleReadonly',value:function(){return this.options.readonly=!this.options.readonly,this.$element.prop('readonly',this.options.readonly),this.$wrapper.toggleClass(this._getClass('readonly')),this.$element}},{key:'indeterminate',value:function(l){return'undefined'==typeof l?this.options.indeterminate:this.options.indeterminate===!!l?this.$element:this.toggleIndeterminate()}},{key:'toggleIndeterminate',value:function(){return this.options.indeterminate=!this.options.indeterminate,this.$element.prop('indeterminate',this.options.indeterminate),this.$wrapper.toggleClass(this._getClass('indeterminate')),this._containerPosition(),this.$element}},{key:'inverse',value:function(l){return'undefined'==typeof l?this.options.inverse:this.options.inverse===!!l?this.$element:this.toggleInverse()}},{key:'toggleInverse',value:function(){this.$wrapper.toggleClass(this._getClass('inverse'));var l=this.$on.clone(!0),m=this.$off.clone(!0);return this.$on.replaceWith(m),this.$off.replaceWith(l),this.$on=m,this.$off=l,this.options.inverse=!this.options.inverse,this.$element}},{key:'onColor',value:function(l){return'undefined'==typeof l?this.options.onColor:(this.options.onColor&&this.$on.removeClass(this._getClass(this.options.onColor)),this.$on.addClass(this._getClass(l)),this.options.onColor=l,this.$element)}},{key:'offColor',value:function(l){return'undefined'==typeof l?this.options.offColor:(this.options.offColor&&this.$off.removeClass(this._getClass(this.options.offColor)),this.$off.addClass(this._getClass(l)),this.options.offColor=l,this.$element)}},{key:'onText',value:function(l){return'undefined'==typeof l?this.options.onText:(this.$on.html(l),this._width(),this._containerPosition(),this.options.onText=l,this.$element)}},{key:'offText',value:function(l){return'undefined'==typeof l?this.options.offText:(this.$off.html(l),this._width(),this._containerPosition(),this.options.offText=l,this.$element)}},{key:'labelText',value:function(l){return'undefined'==typeof l?this.options.labelText:(this.$label.html(l),this._width(),this.options.labelText=l,this.$element)}},{key:'handleWidth',value:function(l){return'undefined'==typeof l?this.options.handleWidth:(this.options.handleWidth=l,this._width(),this._containerPosition(),this.$element)}},{key:'labelWidth',value:function(l){return'undefined'==typeof l?this.options.labelWidth:(this.options.labelWidth=l,this._width(),this._containerPosition(),this.$element)}},{key:'baseClass',value:function(){return this.options.baseClass}},{key:'wrapperClass',value:function(l){return'undefined'==typeof l?this.options.wrapperClass:(l||(l=g.fn.bootstrapSwitch.defaults.wrapperClass),this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(' ')),this.$wrapper.addClass(this._getClasses(l).join(' ')),this.options.wrapperClass=l,this.$element)}},{key:'radioAllOff',value:function(l){if('undefined'==typeof l)return this.options.radioAllOff;var m=!!l;return this.options.radioAllOff===m?this.$element:(this.options.radioAllOff=m,this.$element)}},{key:'onInit',value:function(l){return'undefined'==typeof l?this.options.onInit:(l||(l=g.fn.bootstrapSwitch.defaults.onInit),this.options.onInit=l,this.$element)}},{key:'onSwitchChange',value:function(l){return'undefined'==typeof l?this.options.onSwitchChange:(l||(l=g.fn.bootstrapSwitch.defaults.onSwitchChange),this.options.onSwitchChange=l,this.$element)}},{key:'destroy',value:function(){var l=this.$element.closest('form');return l.length&&l.off('reset.bootstrapSwitch').removeData('bootstrap-switch'),this.$container.children().not(this.$element).remove(),this.$element.unwrap().unwrap().off('.bootstrapSwitch').removeData('bootstrap-switch'),this.$element}},{key:'_getElementOptions',value:function(){return{state:this.$element.is(':checked'),size:this.$element.data('size'),animate:this.$element.data('animate'),disabled:this.$element.is(':disabled'),readonly:this.$element.is('[readonly]'),indeterminate:this.$element.data('indeterminate'),inverse:this.$element.data('inverse'),radioAllOff:this.$element.data('radio-all-off'),onColor:this.$element.data('on-color'),offColor:this.$element.data('off-color'),onText:this.$element.data('on-text'),offText:this.$element.data('off-text'),labelText:this.$element.data('label-text'),handleWidth:this.$element.data('handle-width'),labelWidth:this.$element.data('label-width'),baseClass:this.$element.data('base-class'),wrapperClass:this.$element.data('wrapper-class')}}},{key:'_width',value:function(){var l=this,m=this.$on.add(this.$off).add(this.$label).css('width',''),n='auto'===this.options.handleWidth?Math.round(Math.max(this.$on.width(),this.$off.width())):this.options.handleWidth;return m.width(n),this.$label.width(function(o,p){return'auto'===l.options.labelWidth?p<n?n:p:l.options.labelWidth}),this._handleWidth=this.$on.outerWidth(),this._labelWidth=this.$label.outerWidth(),this.$container.width(2*this._handleWidth+this._labelWidth),this.$wrapper.width(this._handleWidth+this._labelWidth)}},{key:'_containerPosition',value:function(){var l=this,m=0<arguments.length&&void 0!==arguments[0]?arguments[0]:this.options.state,n=arguments[1];this.$container.css('margin-left',function(){var o=[0,'-'+l._handleWidth+'px'];return l.options.indeterminate?'-'+l._handleWidth/2+'px':m?l.options.inverse?o[1]:o[0]:l.options.inverse?o[0]:o[1]})}},{key:'_init',value:function(){var l=this,m=function(){l.setPrevOptions(),l._width(),l._containerPosition(),setTimeout(function(){if(l.options.animate)return l.$wrapper.addClass(l._getClass('animate'))},50)};if(this.$wrapper.is(':visible'))return void m();var n=window.setInterval(function(){if(l.$wrapper.is(':visible'))return m(),window.clearInterval(n)},50)}},{key:'_elementHandlers',value:function(){var l=this;return this.$element.on({'setPreviousOptions.bootstrapSwitch':this.setPrevOptions.bind(this),'previousState.bootstrapSwitch':function(){l.options=l.prevOptions,l.options.indeterminate&&l.$wrapper.addClass(l._getClass('indeterminate')),l.$element.prop('checked',l.options.state).trigger('change.bootstrapSwitch',!0)},'change.bootstrapSwitch':function(n,o){n.preventDefault(),n.stopImmediatePropagation();var p=l.$element.is(':checked');l._containerPosition(p),p===l.options.state||(l.options.state=p,l.$wrapper.toggleClass(l._getClass('off')).toggleClass(l._getClass('on')),!o&&(l.$element.is(':radio')&&g('[name="'+l.$element.attr('name')+'"]').not(l.$element).prop('checked',!1).trigger('change.bootstrapSwitch',!0),l.$element.trigger('switchChange.bootstrapSwitch',[p])))},'focus.bootstrapSwitch':function(n){n.preventDefault(),l.$wrapper.addClass(l._getClass('focused'))},'blur.bootstrapSwitch':function(n){n.preventDefault(),l.$wrapper.removeClass(l._getClass('focused'))},'keydown.bootstrapSwitch':function(n){!n.which||l.options.disabled||l.options.readonly||(37===n.which||39===n.which)&&(n.preventDefault(),n.stopImmediatePropagation(),l.state(39===n.which))}})}},{key:'_handleHandlers',value:function(){var l=this;return this.$on.on('click.bootstrapSwitch',function(m){return m.preventDefault(),m.stopPropagation(),l.state(!1),l.$element.trigger('focus.bootstrapSwitch')}),this.$off.on('click.bootstrapSwitch',function(m){return m.preventDefault(),m.stopPropagation(),l.state(!0),l.$element.trigger('focus.bootstrapSwitch')})}},{key:'_labelHandlers',value:function(){var l=this;this.$label.on({click:function(o){o.stopPropagation()},'mousedown.bootstrapSwitch touchstart.bootstrapSwitch':function(o){l._dragStart||l.options.disabled||l.options.readonly||(o.preventDefault(),o.stopPropagation(),l._dragStart=(o.pageX||o.originalEvent.touches[0].pageX)-parseInt(l.$container.css('margin-left'),10),l.options.animate&&l.$wrapper.removeClass(l._getClass('animate')),l.$element.trigger('focus.bootstrapSwitch'))},'mousemove.bootstrapSwitch touchmove.bootstrapSwitch':function(o){if(null!=l._dragStart){var p=(o.pageX||o.originalEvent.touches[0].pageX)-l._dragStart;o.preventDefault(),p<-l._handleWidth||0<p||(l._dragEnd=p,l.$container.css('margin-left',l._dragEnd+'px'))}},'mouseup.bootstrapSwitch touchend.bootstrapSwitch':function(o){if(l._dragStart){if(o.preventDefault(),l.options.animate&&l.$wrapper.addClass(l._getClass('animate')),l._dragEnd){var p=l._dragEnd>-(l._handleWidth/2);l._dragEnd=!1,l.state(l.options.inverse?!p:p)}else l.state(!l.options.state);l._dragStart=!1}},'mouseleave.bootstrapSwitch':function(){l.$label.trigger('mouseup.bootstrapSwitch')}})}},{key:'_externalLabelHandler',value:function(){var l=this,m=this.$element.closest('label');m.on('click',function(n){n.preventDefault(),n.stopImmediatePropagation(),n.target===m[0]&&l.toggleState()})}},{key:'_formHandler',value:function(){var l=this.$element.closest('form');l.data('bootstrap-switch')||l.on('reset.bootstrapSwitch',function(){window.setTimeout(function(){l.find('input').filter(function(){return g(this).data('bootstrap-switch')}).each(function(){return g(this).bootstrapSwitch('state',this.checked)})},1)}).data('bootstrap-switch',!0)}},{key:'_getClass',value:function(l){return this.options.baseClass+'-'+l}},{key:'_getClasses',value:function(l){return g.isArray(l)?l.map(this._getClass.bind(this)):[this._getClass(l)]}}]),j}();g.fn.bootstrapSwitch=function(j){for(var l=arguments.length,m=Array(1<l?l-1:0),n=1;n<l;n++)m[n-1]=arguments[n];return Array.prototype.reduce.call(this,function(o,p){var q=g(p),r=q.data('bootstrap-switch'),s=r||new h(p,j);return r||q.data('bootstrap-switch',s),'string'==typeof j?s[j].apply(s,m):o},this)},g.fn.bootstrapSwitch.Constructor=h,g.fn.bootstrapSwitch.defaults={state:!0,size:null,animate:!0,disabled:!1,readonly:!1,indeterminate:!1,inverse:!1,radioAllOff:!1,onColor:'primary',offColor:'default',onText:'ON',offText:'OFF',labelText:'&nbsp',handleWidth:'auto',labelWidth:'auto',baseClass:'bootstrap-switch',wrapperClass:'wrapper',onInit:function(){},onSwitchChange:function(){}}});
513
-
514
- /* caret v1.3.3 | https://github.com/accursoft/caret | Copyright (c) 2009 Gideon Sireling. Released under the BSD3 license. */
515
- !function(e){e.fn.caret=function(e){var t=this[0],n="true"===t.contentEditable;if(0==arguments.length){if(window.getSelection){if(n){t.focus();var o=window.getSelection().getRangeAt(0),r=o.cloneRange();return r.selectNodeContents(t),r.setEnd(o.endContainer,o.endOffset),r.toString().length}return t.selectionStart}if(document.selection){if(t.focus(),n){var o=document.selection.createRange(),r=document.body.createTextRange();return r.moveToElementText(t),r.setEndPoint("EndToEnd",o),r.text.length}var e=0,c=t.createTextRange(),r=document.selection.createRange().duplicate(),a=r.getBookmark();for(c.moveToBookmark(a);0!==c.moveStart("character",-1);)e++;return e}return t.selectionStart?t.selectionStart:0}if(-1==e&&(e=this[n?"text":"val"]().length),window.getSelection)n?(t.focus(),window.getSelection().collapse(t.firstChild,e)):t.setSelectionRange(e,e);else if(document.body.createTextRange)if(n){var c=document.body.createTextRange();c.moveToElementText(t),c.moveStart("character",e),c.collapse(!0),c.select()}else{var c=t.createTextRange();c.move("character",e),c.select()}return n||t.focus(),e}}(jQuery);
516
-
517
- /* jQuery tagEditor v1.0.21 | https://github.com/Pixabay/jQuery-tagEditor | Copyright (c) 2013 Pixabay. Licensed under the MIT license. */
 
 
 
518
  !function(t){t.fn.tagEditorInput=function(){var e=" ",i=t(this),a=parseInt(i.css("fontSize")),r=t("<span/>").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:i.css("fontSize"),fontFamily:i.css("fontFamily"),fontWeight:i.css("fontWeight"),letterSpacing:i.css("letterSpacing"),whiteSpace:"nowrap"}),l=function(){if(e!==(e=i.val())){r.text(e);var t=r.width()+a;20>t&&(t=20),t!=i.width()&&i.width(t)}};return r.insertAfter(i),i.bind("keyup keydown focus",l)},t.fn.tagEditor=function(e,a,r){function l(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}var n,o=t.extend({},t.fn.tagEditor.defaults,e),c=this;if(o.dregex=new RegExp("["+o.delimiter.replace("-","-")+"]","g"),"string"==typeof e){var s=[];return c.each(function(){var i=t(this),l=i.data("options"),n=i.next(".tag-editor");if("getTags"==e)s.push({field:i[0],editor:n,tags:n.data("tags")});else if("addTag"==e){if(l.maxTags&&n.data("tags").length>=l.maxTags)return!1;t('<li><div class="tag-editor-spacer">&nbsp;'+l.delimiter[0]+'</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>').appendTo(n).find(".tag-editor-tag").html('<input type="text" maxlength="'+l.maxLength+'">').addClass("active").find("input").val(a).blur(),r?t(".placeholder",n).remove():n.click()}else"removeTag"==e?(t(".tag-editor-tag",n).filter(function(){return t(this).text()==a}).closest("li").find(".tag-editor-delete").click(),r||n.click()):"destroy"==e&&i.removeClass("tag-editor-hidden-src").removeData("options").off("focus.tag-editor").next(".tag-editor").remove()}),"getTags"==e?s:this}return window.getSelection&&t(document).off("keydown.tag-editor").on("keydown.tag-editor",function(e){if(8==e.which||46==e.which||e.ctrlKey&&88==e.which){try{var a=getSelection(),r="INPUT"!=document.activeElement.tagName?t(a.getRangeAt(0).startContainer.parentNode).closest(".tag-editor"):0}catch(e){r=0}if(a.rangeCount>0&&r&&r.length){var l=[],n=a.toString().split(r.prev().data("options").dregex);for(i=0;i<n.length;i++){var o=t.trim(n[i]);o&&l.push(o)}return t(".tag-editor-tag",r).each(function(){~t.inArray(t(this).text(),l)&&t(this).closest("li").find(".tag-editor-delete").click()}),!1}}}),c.each(function(){function e(){!o.placeholder||c.length||t(".deleted, .placeholder, input",s).length||s.append('<li class="placeholder"><div>'+o.placeholder+"</div></li>")}function i(i){var a=c.toString();c=t(".tag-editor-tag:not(.deleted)",s).map(function(e,i){var a=t.trim(t(this).hasClass("active")?t(this).find("input").val():t(i).text());return a?a:void 0}).get(),s.data("tags",c),r.val(c.join(o.delimiter[0])),i||a!=c.toString()&&o.onChange(r,s,c),e()}function a(e){for(var a,n=e.closest("li"),d=e.val().replace(/ +/," ").split(o.dregex),g=e.data("old_tag"),f=c.slice(0),h=!1,u=0;u<d.length;u++)if(v=t.trim(d[u]).slice(0,o.maxLength),o.forceLowercase&&(v=v.toLowerCase()),a=o.beforeTagSave(r,s,f,g,v),v=a||v,a!==!1&&v&&(o.removeDuplicates&&~t.inArray(v,f)&&t(".tag-editor-tag",s).each(function(){t(this).text()==v&&t(this).closest("li").remove()}),f.push(v),n.before('<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag">'+l(v)+'</div><div class="tag-editor-delete"><i></i></div></li>'),o.maxTags&&f.length>=o.maxTags)){h=!0;break}e.attr("maxlength",o.maxLength).removeData("old_tag").val(""),h?e.blur():e.focus(),i()}var r=t(this),c=[],s=t("<ul "+(o.clickDelete?'oncontextmenu="return false;" ':"")+'class="tag-editor"></ul>').insertAfter(r);r.addClass("tag-editor-hidden-src").data("options",o).on("focus.tag-editor",function(){s.click()}),s.append('<li style="width:1px">&nbsp;</li>');var d='<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>';s.click(function(e,i){var a,r,l=99999;if(!window.getSelection||""==getSelection())return o.maxTags&&s.data("tags").length>=o.maxTags?(s.find("input").blur(),!1):(n=!0,t("input:focus",s).blur(),n?(n=!0,t(".placeholder",s).remove(),i&&i.length?r="before":t(".tag-editor-tag",s).each(function(){var n=t(this),o=n.offset(),c=o.left,s=o.top;e.pageY>=s&&e.pageY<=s+n.height()&&(e.pageX<c?(r="before",a=c-e.pageX):(r="after",a=e.pageX-c-n.width()),l>a&&(l=a,i=n))}),"before"==r?t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click():"after"==r?t(d).insertAfter(i.closest("li")).find(".tag-editor-tag").click():t(d).appendTo(s).find(".tag-editor-tag").click(),!1):!1)}),s.on("click",".tag-editor-delete",function(){if(t(this).prev().hasClass("active"))return t(this).closest("li").find("input").caret(-1),!1;var a=t(this).closest("li"),l=a.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,l.text())===!1?!1:(l.addClass("deleted").animate({width:0},o.animateDelete,function(){a.remove(),e()}),i(),!1)}),o.clickDelete&&s.on("mousedown",".tag-editor-tag",function(a){if(a.ctrlKey||a.which>1){var l=t(this).closest("li"),n=l.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,n.text())===!1?!1:(n.addClass("deleted").animate({width:0},o.animateDelete,function(){l.remove(),e()}),i(),!1)}}),s.on("click",".tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1))return!1;if(!t(this).hasClass("active")){var i=t(this).text(),a=Math.abs((t(this).offset().left-e.pageX)/t(this).width()),r=parseInt(i.length*a),n=t(this).html('<input type="text" maxlength="'+o.maxLength+'" value="'+l(i)+'">').addClass("active").find("input");if(n.data("old_tag",i).tagEditorInput().focus().caret(r),o.autocomplete){var c=t.extend({},o.autocomplete),d="select"in c?o.autocomplete.select:"";c.select=function(e,i){d&&d(e,i),setTimeout(function(){s.trigger("click",[t(".active",s).find("input").closest("li").next("li").find(".tag-editor-tag")])},20)},n.autocomplete(c)}}return!1}),s.on("blur","input",function(d){d.stopPropagation();var g=t(this),f=g.data("old_tag"),h=t.trim(g.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(h){if(h.indexOf(o.delimiter[0])>=0)return void a(g);if(h!=f)if(o.forceLowercase&&(h=h.toLowerCase()),cb_val=o.beforeTagSave(r,s,c,f,h),h=cb_val||h,cb_val===!1){if(f)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}else o.removeDuplicates&&t(".tag-editor-tag:not(.active)",s).each(function(){t(this).text()==h&&t(this).closest("li").remove()})}else{if(f&&o.beforeTagDelete(r,s,c,f)===!1)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}g.parent().html(l(h)).removeClass("active"),h!=f&&i(),e()});var g;s.on("paste","input",function(){t(this).removeAttr("maxlength"),g=t(this),setTimeout(function(){a(g)},30)});var f;s.on("keypress","input",function(e){o.delimiter.indexOf(String.fromCharCode(e.which))>=0&&(f=t(this),setTimeout(function(){a(f)},20))}),s.on("keydown","input",function(e){var i=t(this);if((37==e.which||!o.autocomplete&&38==e.which)&&!i.caret()||8==e.which&&!i.val()){var a=i.closest("li").prev("li").find(".tag-editor-tag");return a.length?a.click().find("input").caret(-1):!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags||t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click(),!1}if((39==e.which||!o.autocomplete&&40==e.which)&&i.caret()==i.val().length){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(9==e.which){if(e.shiftKey){var a=i.closest("li").prev("li").find(".tag-editor-tag");if(a.length)a.click().find("input").caret(0);else{if(!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags)return r.attr("disabled","disabled"),void setTimeout(function(){r.removeAttr("disabled")},30);t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click()}return!1}var l=i.closest("li").next("li").find(".tag-editor-tag");if(l.length)l.click().find("input").caret(0);else{if(!i.val())return;s.click()}return!1}if(!(46!=e.which||t.trim(i.val())&&i.caret()!=i.val().length)){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(13==e.which)return s.trigger("click",[i.closest("li").next("li").find(".tag-editor-tag")]),o.maxTags&&s.data("tags").length>=o.maxTags&&s.find("input").blur(),!1;if(36!=e.which||i.caret()){if(35==e.which&&i.caret()==i.val().length)s.find(".tag-editor-tag").last().click();else if(27==e.which)return i.val(i.data("old_tag")?i.data("old_tag"):"").blur(),!1}else s.find(".tag-editor-tag").first().click()});for(var h=o.initialTags.length?o.initialTags:r.val().split(o.dregex),u=0;u<h.length&&!(o.maxTags&&u>=o.maxTags);u++){var v=t.trim(h[u].replace(/ +/," "));v&&(o.forceLowercase&&(v=v.toLowerCase()),c.push(v),s.append('<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag">'+l(v)+'</div><div class="tag-editor-delete"><i></i></div></li>'))}i(!0),o.sortable&&t.fn.sortable&&s.sortable({distance:5,cancel:".tag-editor-spacer, input",helper:"clone",update:function(){i()}})})},t.fn.tagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,delimiter:",;",placeholder:"",forceLowercase:!0,removeDuplicates:!0,clickDelete:!1,animateDelete:175,sortable:!0,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}}(jQuery);
1
+ if ( typeof SlimStatAdminParams == 'undefined' ) {
2
+ SlimStatAdminParams = {
3
+ refresh_interval: 0,
4
+ expand_details: 'no',
5
+ datepicker_image: '',
6
+ text_direction: ''
7
+ };
8
+ }
9
+
10
+ // ----- TABLE OF CONTENTS -----------------------------------------------------------
11
+ //
12
+ // 1. Data refresh
13
+ // 2. Filters
14
+ // 3. Activity log
15
+ // 4. Customizer
16
+ // 5. Miscellaneous
17
+ // 6. Init third-party libraries
18
+ //
19
+ // -----------------------------------------------------------------------------------
20
+
21
+ jQuery( function() {
22
+
23
+ // ----- BEGIN: DATA REFRESH -----------------------------------------------------
24
+ //
25
+
26
+ // Reload a report's data if it is (re)activated via the checkbox under Screen Options
27
+ jQuery( 'input.hide-postbox-tog[id^=slim_p]' ).on( 'click.postboxes', function () {
28
+ if ( jQuery( this ).prop( "checked" ) && jQuery( '#' + jQuery( this ).val() ).length ) {
29
+ SlimStatAdmin.refresh_report( jQuery( this ).val() );
30
+ }
31
+ });
32
+
33
+ // Reload a report's data if the corresponding refresh/pagination button is clicked
34
+ jQuery( document ).on( 'click', '[id^=slim_] .refresh', function( e ) {
35
+ e.preventDefault();
36
+
37
+ var id = jQuery( this ).parents( '.postbox' ).attr( 'id' );
38
+
39
+ // Is this a pagination link?
40
+ if ( typeof jQuery( this ).attr( 'href' ).split( '?' )[ 1 ] == 'string' ) {
41
+ clean_filters = SlimStatAdmin.get_query_string_filters( jQuery( this ).attr( 'href' ).split( '?' )[ 1 ].substring( 1 ) );
42
+ if ( typeof clean_filters[ 'fs[start_from]' ] == 'string' ) {
43
+ jQuery( '<input type="hidden" name="fs[start_from]" class="slimstat-post-filter slimstat-temp-filter" value="' + clean_filters[ 'fs[start_from]' ] + '">' ).appendTo( '#slimstat-filters-form' );
44
+ }
45
+ }
46
+
47
+ SlimStatAdmin.refresh_report( id );
48
+
49
+ // Remove any temporary filters (pagination) set here above
50
+ jQuery( '.slimstat-temp-filter' ).remove();
51
+
52
+ // Re-initialize SlimScroll on the new content
53
+ jQuery( '#' + id + ' .inside' ).slimScroll( {
54
+ scrollTo : '0px'
55
+ } );
56
+ } );
57
+
58
+ // Asynchronous reports are loaded dynamically after the page loads
59
+ if ( SlimStatAdminParams.async_load == 'on' ) {
60
+ jQuery( 'div[id^=slim_]' ).each( function() {
61
+ SlimStatAdmin.refresh_report( jQuery( this ).attr( 'id' ) );
62
+ } );
63
+ }
64
+
65
+ //
66
+ // ----- END: DATA REFRESH -------------------------------------------------------
67
+
68
+ // ----- BEGIN: FILTERS ----------------------------------------------------------
69
+ //
70
+
71
+ // Make input field read-only if certain operators are selected
72
+ jQuery( '#slimstat-filter-operator' ).on( 'change', function() {
73
+ if ( this.value == 'is_empty' || this.value == 'is_not_empty' ) {
74
+ jQuery( '#slimstat-filter-value' ).attr( 'readonly', 'readonly' );
75
+ }
76
+ else {
77
+ jQuery( '#slimstat-filter-value' ).removeAttr( 'readonly' );
78
+ }
79
+ } );
80
+
81
+ // Toggle the Date Filters dropdown menu
82
+ jQuery( '#slimstat-date-filters > a' ).on( 'click', function( e ) {
83
+ e.preventDefault();
84
+ jQuery( '#slimstat-date-filters > .dropdown' ).slideToggle( 250 );
85
+ } );
86
+
87
+ // Initialize the datepicker library and button (built-in)
88
+ if ( typeof jQuery( '.slimstat-filter-date' ).datepicker == 'function' ) {
89
+ jQuery( '.slimstat-filter-date' ).datepicker( {
90
+ buttonImage: SlimStatAdminParams.datepicker_image,
91
+ buttonImageOnly: true,
92
+ changeMonth: true,
93
+ changeYear: true,
94
+ dateFormat: 'yy-m-d',
95
+ maxDate: new Date,
96
+ nextText: '&raquo;',
97
+ prevText: '&laquo;',
98
+ showOn: 'both',
99
+
100
+ onClose: function( dateText, inst ) {
101
+ if ( dateText.length ) {
102
+ jQuery( '#slimstat-filter-hour' ).val( 0 );
103
+ jQuery( '#slimstat-filter-day' ).val( dateText.split( '-' )[ 2 ] );
104
+ jQuery( '#slimstat-filter-month' ).val( dateText.split( '-' )[ 1 ] );
105
+ jQuery( '#slimstat-filter-year' ).val( dateText.split( '-' )[ 0 ] );
106
+ jQuery( '#slimstat-filter-interval' ).val( -1 );
107
+ }
108
+ }
109
+ } );
110
+ }
111
+
112
+ // Save filters
113
+ jQuery( document ).on( 'click', '#slimstat-save-filter', function( e ) {
114
+ e.preventDefault();
115
+
116
+ data = {
117
+ action: 'slimstat_manage_filters',
118
+ security: jQuery( '#meta-box-order-nonce' ).val(),
119
+ type: 'save',
120
+ filter_array: jQuery( this ).attr( 'data-filter-array' )
121
+ };
122
+
123
+ var element = jQuery( this );
124
+ jQuery.post( ajaxurl, data, function( response ) {
125
+ element.text( response ).fadeOut( 1500 );
126
+ } );
127
+ } );
128
+
129
+ // Load saved filters: open a dialog with a list of filters
130
+ jQuery( document ).on( 'click', '#slimstat-load-saved-filters', function( e ) {
131
+ e.preventDefault();
132
+ var inner_html = '';
133
+
134
+ data = {
135
+ action: 'slimstat_manage_filters',
136
+ security: jQuery( '#meta-box-order-nonce' ).val(),
137
+ type: 'load',
138
+ page: SlimStatAdmin.get_current_tab()
139
+ };
140
+
141
+ var dialog_title = jQuery( this ).attr( 'title' ); // passed as an attribute so that it can be localized
142
+ jQuery.post( ajaxurl, data, function( response ) {
143
+ jQuery( '#slimstat-modal-dialog' ).dialog( {
144
+ dialogClass: 'slimstat',
145
+ title: dialog_title
146
+ } ).html( response ).dialog( 'open' );
147
+ } );
148
+ } );
149
+
150
+ // Delete saved filters
151
+ jQuery( document ).on( 'click', '.slimstat-delete-filter', function( e ) {
152
+ e.preventDefault();
153
+
154
+ data = {
155
+ action: 'slimstat_manage_filters',
156
+ security: jQuery( '#meta-box-order-nonce' ).val(),
157
+ page: SlimStatAdmin.get_current_tab(),
158
+ type: 'delete',
159
+ filter_id: jQuery( this ).attr( 'data-filter-id' )
160
+ };
161
+
162
+ jQuery.post( ajaxurl, data, function( response ) {
163
+ jQuery( '#slim_filters_overlay' ).parent().html( response );
164
+ } );
165
+ } );
166
+
167
+ // Since we handle all "filter links" as POST requests (see code here above), we need to add dummy form tag to the dashboard,
168
+ // for when our reports are displayed on that page
169
+ if ( !jQuery( '#slimstat-filters-form' ).length ) {
170
+ jQuery( '<form id="slimstat-filters-form" method="post"/>' ).appendTo('body');
171
+ }
172
+
173
+
174
+ jQuery( document ).on( 'click', '.slimstat-filter-link, #toplevel_page_slimview1 a, #wp-admin-bar-slimstat-header li a', function( e ) {
175
+ url = jQuery( this ).attr( 'href' );
176
+
177
+ // If this link doesn't have a valid HREF attribute, bail
178
+ if ( typeof url != 'string' ) {
179
+ return true;
180
+ }
181
+
182
+ e.preventDefault();
183
+
184
+ jQuery( 'form#slimstat-filters-form' ).attr( 'action', url.split( '?' )[ 0 ] + '?page=' + SlimStatAdmin.get_current_tab( url.split( '?' )[ 1 ] ) );
185
+
186
+ SlimStatAdmin.add_url_filters_to_form( url, typeof jQuery( this ).attr( 'data-reset-filters' ) != 'undefined' );
187
+
188
+ jQuery( '#slimstat-filters-form' ).submit();
189
+
190
+ return false;
191
+ });
192
+
193
+ //
194
+ // ----- END: FILTERS ------------------------------------------------------------
195
+
196
+ // ----- BEGIN: ACTIVITY LOG -----------------------------------------------------
197
+ //
198
+
199
+ // Reload the Activity Log every X seconds
200
+ if ( SlimStatAdminParams.refresh_interval > 0 && jQuery( '.refresh-timer' ).length > 0 ) {
201
+ SlimStatAdmin._refresh_timer = parseInt( SlimStatAdminParams.refresh_interval );
202
+ window.setTimeout( "SlimStatAdmin.refresh_countdown();", 1000 );
203
+ }
204
+
205
+ // Delete a pageview when the corresponding button is clicked.
206
+ // Since this content can be reloaded dynamically, we use the .on call with the classname
207
+ jQuery( document ).on( 'click', '.slimstat-delete-entry', function( e ) {
208
+ e.preventDefault();
209
+
210
+ data = {
211
+ action: 'slimstat_delete_pageview',
212
+ security: jQuery( '#meta-box-order-nonce' ).val(),
213
+ pageview_id : jQuery( this ).attr( 'data-pageview-id' )
214
+ };
215
+
216
+ var parent = jQuery( this ).parents( 'p' );
217
+ jQuery.post( ajaxurl, data, function( response ) {
218
+ parent.fadeOut( 500 );
219
+ } );
220
+ } );
221
+
222
+ // // Modal Window / Whois
223
+ jQuery( document ).on( 'click', '.whois', function( e ) {
224
+ e.preventDefault();
225
+
226
+ // If admin is using HTTPS and IP lookup service is not, open a new window/tab, instead of an overlay dialog
227
+ if ( document.location.href.substr( 0, document.location.href.indexOf( "://" ) ).toLowerCase() == 'https' && jQuery( this ).attr( 'href' ).substr( 0, jQuery( this ).attr( 'href' ).indexOf( "://" ) ).toLowerCase() == 'http' ) {
228
+ window.open( jQuery( this ).attr( 'href' ), '_blank' );
229
+ return -1;
230
+ }
231
+
232
+ jQuery('#slimstat-modal-dialog').dialog({
233
+ dialogClass: 'slimstat',
234
+ title: jQuery(this).attr('title')
235
+ }).html('<iframe id="ip2location" src="'+jQuery(this).attr('href')+'" width="100%" height="630"></iframe>');
236
+ jQuery('#slimstat-modal-dialog').dialog('open');
237
+ });
238
+
239
+ //
240
+ // ----- END: ACTIVITY LOG -------------------------------------------------------
241
+
242
+ // ----- BEGIN: CUSTOMIZER -------------------------------------------------------
243
+ //
244
+
245
+ // Clone and delete report placeholders
246
+ jQuery( '.slimstat-layout .slimstat-header-buttons a' ).on( 'click', function() {
247
+ if ( jQuery( this ).hasClass( 'slimstat-font-docs' ) ) {
248
+ jQuery( this ).removeClass( 'slimstat-font-docs' ).addClass( 'slimstat-font-trash' ).parents( '.postbox' ).clone( true ).appendTo( jQuery( this ).parents( '.meta-box-sortables' ) );
249
+ jQuery( this ).removeClass( 'slimstat-font-trash' ).addClass( 'slimstat-font-docs' );
250
+ }
251
+ else if ( jQuery( this ).hasClass( 'slimstat-font-minus-circled' ) ) {
252
+ jQuery( this ).removeClass( 'slimstat-font-minus-circled' ).parents( '.postbox' ).appendTo( jQuery( '#postbox-container-inactive .meta-box-sortables' ) );
253
+ }
254
+ else {
255
+ jQuery( this ).parents( '.postbox' ).remove();
256
+ }
257
+
258
+ // Save the new groups
259
+ var data = {
260
+ action: 'meta-box-order',
261
+ _ajax_nonce: jQuery( '#meta-box-order-nonce' ).val(),
262
+ page: 'slimstat_page_slimlayout',
263
+ page_columns: 0
264
+ };
265
+
266
+ jQuery( '.meta-box-sortables' ).each( function() {
267
+ data[ 'order[' + this.id.split("-")[0] + ']' ] = jQuery( this ).sortable( 'toArray' ).join( ',' );
268
+ });
269
+
270
+ jQuery.post( ajaxurl, data );
271
+ } );
272
+
273
+ //
274
+ // ----- END: CUSTOMIZER ---------------------------------------------------------
275
+
276
+ // ----- BEGIN: MISCELLANEOUS ----------------------------------------------------
277
+ //
278
+
279
+ // Hide a notice and send the corresponding ajax request to the server
280
+ jQuery( document ).on( 'click', '[id^=slimstat-hide-]', function( e ) {
281
+ e.preventDefault();
282
+ jQuery( this ).parents( '.slimstat-notice' ).slideUp( 1000 );
283
+ data = {
284
+ action: jQuery( this ).attr('id').replace(/-/g, '_'),
285
+ security: jQuery( '#meta-box-order-nonce' ).val()
286
+ };
287
+
288
+ jQuery.ajax({
289
+ url: ajaxurl,
290
+ type: 'post',
291
+ async: true,
292
+ data: data
293
+ });
294
+ });
295
+
296
+ //
297
+ // ----- END: MISCELLANEOUS ------------------------------------------------------
298
+
299
+ // ----- BEGIN: INIT THIRD-PARTY LIBRARIES ---------------------------------------
300
+ //
301
+
302
+ // SlimScroll
303
+ jQuery( '[id^=slim_] .inside' ).slimScroll( {
304
+ distance: '2px',
305
+ opacity: '0.15',
306
+ size: '5px',
307
+ wheelStep: 10
308
+ } );
309
+
310
+ // QTip
311
+ jQuery( document ).on( 'mouseover', '.slimstat-tooltip-trigger', function( e ) {
312
+ if ( typeof jQuery( this ).attr( 'data-hasqtip' ) != 'undefined' ) {
313
+ return true;
314
+ }
315
+
316
+ tooltip_content = jQuery( this ).find( '.slimstat-tooltip-content:not(.expanded)' );
317
+
318
+ if ( typeof tooltip_content == 'undefined' || tooltip_content == null || tooltip_content.length == 0 ) {
319
+ tooltip_content = jQuery( this ).attr( 'title' );
320
+ }
321
+ else {
322
+ tooltip_content = tooltip_content[ 0 ].innerHTML;
323
+ }
324
+
325
+ if ( typeof tooltip_content != 'undefined' && tooltip_content.length ) {
326
+ jQuery(this).qtip( {
327
+ overwrite: false,
328
+ content: {
329
+ text: tooltip_content
330
+ },
331
+ show: {
332
+ event: e.type,
333
+ ready: true
334
+ },
335
+ hide: {
336
+ delay: 250,
337
+ fixed: true
338
+ },
339
+ position: {
340
+ my: 'bottom right',
341
+ at: 'top left',
342
+ adjust: {
343
+ x: 5
344
+ },
345
+ viewport: jQuery( window )
346
+ },
347
+ style: {
348
+ classes: 'qtip-dark qtip-rounded'
349
+ }
350
+ }, e );
351
+ }
352
+ } );
353
+
354
+ // Modal Window
355
+ if ( typeof jQuery( '#slimstat-modal-dialog' ).dialog == 'function' ) {
356
+ jQuery( '#slimstat-modal-dialog' ).dialog( {
357
+ autoOpen: false,
358
+ closeOnEscape: true,
359
+ closeText: '',
360
+ draggable: true,
361
+ modal: true,
362
+ open: function(){
363
+ jQuery( '.ui-widget-overlay, .close-dialog' ).on( 'click', function() {
364
+ jQuery( '#slimstat-modal-dialog' ).dialog( 'close' );
365
+ } );
366
+ },
367
+ position: { my: "top center" },
368
+ resizable: false
369
+ } );
370
+ }
371
+
372
+ // BootstrapSwitch (pretty checkboxes in settings)
373
+ jQuery( 'input.slimstat-checkbox-toggle' ).not( '[data-switch-no-init]' ).bootstrapSwitch();
374
+
375
+ // TagEditor
376
+ jQuery( 'textarea.slimstat-taglist' ).tagEditor( {
377
+ forceLowercase: false,
378
+ maxLength: 1000
379
+ } );
380
+
381
+ //
382
+ // ----- END: INIT THIRD-PARTY LIBRARIES -----------------------------------------
383
+ } );
384
+
385
+ // ----- BEGIN: SLIMSTATADMIN HELPER FUNCTIONS ---------------------------------------
386
+ var SlimStatAdmin = {
387
+ _refresh_timer: 0,
388
+
389
+ refresh_countdown: function() {
390
+ SlimStatAdmin._refresh_timer--;
391
+ minutes = parseInt( SlimStatAdmin._refresh_timer / 60 );
392
+ seconds = parseInt( SlimStatAdmin._refresh_timer % 60 );
393
+
394
+ jQuery( '.refresh-timer' ).html( minutes + ':' + ( ( seconds < 10 ) ? '0' : '' ) + seconds );
395
+
396
+ if ( SlimStatAdmin._refresh_timer > 0 ) {
397
+ window.setTimeout( SlimStatAdmin.refresh_countdown, 1000 );
398
+ }
399
+ else {
400
+ // Request the data from the server
401
+ SlimStatAdmin.refresh_report( 'slim_p7_02' );
402
+
403
+ // Reset the countdown timer
404
+ SlimStatAdmin._refresh_timer = parseInt( SlimStatAdminParams.refresh_interval );
405
+ window.setTimeout( "SlimStatAdmin.refresh_countdown();", 1000 );
406
+ }
407
+ },
408
+
409
+ refresh_report: function( id ) {
410
+ var inner_content = '#' + id + ' .inside';
411
+ jQuery( inner_content ).html( '<p class="loading"><i class="slimstat-font-spin4 animate-spin"></i></p>' );
412
+ data = {
413
+ action: 'slimstat_load_report',
414
+ security: jQuery( '#meta-box-order-nonce' ).val(),
415
+ page: SlimStatAdmin.get_current_tab(),
416
+ report_id: id
417
+ };
418
+
419
+ // Append the data from the hidden form
420
+ filters_input = jQuery( '#slimstat-filters-form .slimstat-post-filter' ).toArray();
421
+ for ( i in filters_input ) {
422
+ data[ filters_input[ i ][ 'name' ] ] = filters_input[ i ][ 'value' ];
423
+ }
424
+
425
+ jQuery.post( ajaxurl, data, function( response ) {
426
+ // Charts don't play nice with the "fade" animation we have for the other reports
427
+ if ( id.indexOf( '_01' ) > 0 ) {
428
+ jQuery( inner_content ).html( response );
429
+ }
430
+ else{
431
+ jQuery( inner_content ).fadeOut( 500, function() { jQuery( this ).html( response ).fadeIn( 500 ); } );
432
+
433
+ // If we are refreshing the Activity Log, let's reset the countdown timer
434
+ if ( id == 'slim_p7_02' ) {
435
+ SlimStatAdmin._refresh_timer = SlimStatAdminParams.refresh_interval;
436
+ }
437
+ }
438
+ } );
439
+ },
440
+
441
+ get_query_string_filters: function( url ) {
442
+
443
+ // Parse the URL and update the form_filters object accordingly
444
+ query_string_pairs = url.substring( url.indexOf( '?' ) + 1 ).split( '&' );
445
+ clean_filters = {};
446
+
447
+ for ( i in query_string_pairs ) {
448
+ decoded_pair = decodeURIComponent( query_string_pairs[ i ].replace( /\+/g, '%20' ) );
449
+
450
+ if ( decoded_pair.indexOf( 'fs[' ) == -1 ) {
451
+ continue;
452
+ }
453
+
454
+ a_pair = decoded_pair.split( '=' );
455
+ if ( a_pair[ 0 ].length ) {
456
+ clean_filters[ a_pair[ 0 ] ] = a_pair[ 1 ];
457
+ }
458
+ }
459
+
460
+ return clean_filters;
461
+ },
462
+
463
+ add_url_filters_to_form: function( url, delete_existing_filters ) {
464
+ clean_filters = SlimStatAdmin.get_query_string_filters( url );
465
+
466
+ // Manipulate the existing list of filters (hidden input fields), if we don't want to delete them
467
+ if ( typeof delete_existing_filters == 'undefined' || !delete_existing_filters ) {
468
+
469
+ for( i in clean_filters ) {
470
+ // If value is empty (length is 1, meaning that it just has the operator but no value), delete corresponding input field
471
+ if ( clean_filters[ i ].trim().split( ' ' ).length == 1 ) {
472
+ jQuery( 'input[name="' + i + '"]' ).remove();
473
+ }
474
+ else if ( jQuery( 'input[name="' + i + '"]' ).length > 0 ) {
475
+ jQuery( 'input[name="' + i + '"]' ).attr( 'value', clean_filters[ i ] );
476
+ }
477
+ else {
478
+ jQuery( '<input type="hidden" name="' + i + '" class="slimstat-post-filter" value="' + clean_filters[ i ] + '">' ).appendTo( '#slimstat-filters-form' );
479
+ }
480
+ }
481
+ }
482
+ // Start from a clean slate
483
+ else {
484
+ jQuery( '.slimstat-post-filter' ).remove();
485
+
486
+ for( i in clean_filters ) {
487
+ jQuery( '<input type="hidden" name="' + i + '" class="slimstat-post-filter" value="' + clean_filters[ i ] + '">' ).appendTo( '#slimstat-filters-form' );
488
+ }
489
+ }
490
+ },
491
+
492
+ // Get the value of a given key in the query string passed via the URL
493
+ get_current_tab: function( query_string ) {
494
+ query_string = typeof query_string == 'undefined' ? window.location.search.substring( 1 ) : query_string;
495
+ var regex = new RegExp( "page(=([^&#]*)|&|#|$)" );
496
+
497
+ values = regex.exec( query_string );
498
+
499
+ if ( typeof values != 'undefined' && values != null && typeof values[ 2 ] == 'string' ) {
500
+ return decodeURIComponent( values[ 2 ].replace( /\+/g, " " ) );
501
+ }
502
+
503
+ return '';
504
+ }
505
+ }
506
+ // ----- END: SLIMSTATADMIN HELPER FUNCTIONS -----------------------------------------
507
+
508
+ /* SlimScroll v1.3.8 | http://rocha.la | Copyright (c) 2011 Piotr Rochala. Released under the MIT and GPL licenses. */
509
+ !function(e){e.fn.extend({slimScroll:function(i){var s={width:"auto",size:"7px",color:"#000",position:"right",distance:"1px",start:"top",opacity:.4,alwaysVisible:!1,disableFadeOut:!1,railVisible:!1,railColor:"#333",railOpacity:.2,railDraggable:!0,railClass:"slimScrollRail",barClass:"slimScrollBar",wrapperClass:"slimScrollDiv",allowPageScroll:!1,wheelStep:20,touchScrollStep:200,borderRadius:"7px",railBorderRadius:"7px"},o=e.extend(s,i);return this.each(function(){function s(t){if(h){var t=t||window.event,i=0;t.wheelDelta&&(i=-t.wheelDelta/120),t.detail&&(i=t.detail/3);var s=t.target||t.srcTarget||t.srcElement;e(s).closest("."+o.wrapperClass).is(x.parent())&&r(i,!0),t.preventDefault&&!y&&t.preventDefault(),y||(t.returnValue=!1)}}function r(e,t,i){y=!1;var s=e,r=x.outerHeight()-D.outerHeight();if(t&&(s=parseInt(D.css("top"))+e*parseInt(o.wheelStep)/100*D.outerHeight(),s=Math.min(Math.max(s,0),r),s=e>0?Math.ceil(s):Math.floor(s),D.css({top:s+"px"})),v=parseInt(D.css("top"))/(x.outerHeight()-D.outerHeight()),s=v*(x[0].scrollHeight-x.outerHeight()),i){s=e;var a=s/x[0].scrollHeight*x.outerHeight();a=Math.min(Math.max(a,0),r),D.css({top:a+"px"})}x.scrollTop(s),x.trigger("slimscrolling",~~s),n(),c()}function a(e){window.addEventListener?(e.addEventListener("DOMMouseScroll",s,!1),e.addEventListener("mousewheel",s,!1)):document.attachEvent("onmousewheel",s)}function l(){f=Math.max(x.outerHeight()/x[0].scrollHeight*x.outerHeight(),m),D.css({height:f+"px"});var e=f==x.outerHeight()?"none":"block";D.css({display:e})}function n(){if(l(),clearTimeout(p),v==~~v){if(y=o.allowPageScroll,b!=v){var e=0==~~v?"top":"bottom";x.trigger("slimscroll",e)}}else y=!1;return b=v,f>=x.outerHeight()?void(y=!0):(D.stop(!0,!0).fadeIn("fast"),void(o.railVisible&&R.stop(!0,!0).fadeIn("fast")))}function c(){o.alwaysVisible||(p=setTimeout(function(){o.disableFadeOut&&h||u||d||(D.fadeOut("slow"),R.fadeOut("slow"))},1e3))}var h,u,d,p,g,f,v,b,w="<div></div>",m=30,y=!1,x=e(this);if(x.parent().hasClass(o.wrapperClass)){var C=x.scrollTop();if(D=x.siblings("."+o.barClass),R=x.siblings("."+o.railClass),l(),e.isPlainObject(i)){if("height"in i&&"auto"==i.height){x.parent().css("height","auto"),x.css("height","auto");var H=x.parent().parent().height();x.parent().css("height",H),x.css("height",H)}else if("height"in i){var S=i.height;x.parent().css("height",S),x.css("height",S)}if("scrollTo"in i)C=parseInt(o.scrollTo);else if("scrollBy"in i)C+=parseInt(o.scrollBy);else if("destroy"in i)return D.remove(),R.remove(),void x.unwrap();r(C,!1,!0)}}else if(!(e.isPlainObject(i)&&"destroy"in i)){o.height="auto"==o.height?x.parent().height():o.height;var E=e(w).addClass(o.wrapperClass).css({position:"relative",overflow:"hidden",width:o.width,height:o.height});x.css({overflow:"hidden",width:o.width,height:o.height});var R=e(w).addClass(o.railClass).css({width:o.size,height:"100%",position:"absolute",top:0,display:o.alwaysVisible&&o.railVisible?"block":"none","border-radius":o.railBorderRadius,background:o.railColor,opacity:o.railOpacity,zIndex:90}),D=e(w).addClass(o.barClass).css({background:o.color,width:o.size,position:"absolute",top:0,opacity:o.opacity,display:o.alwaysVisible?"block":"none","border-radius":o.borderRadius,BorderRadius:o.borderRadius,MozBorderRadius:o.borderRadius,WebkitBorderRadius:o.borderRadius,zIndex:99}),M="right"==o.position?{right:o.distance}:{left:o.distance};R.css(M),D.css(M),x.wrap(E),x.parent().append(D),x.parent().append(R),o.railDraggable&&D.bind("mousedown",function(i){var s=e(document);return d=!0,t=parseFloat(D.css("top")),pageY=i.pageY,s.bind("mousemove.slimscroll",function(e){currTop=t+e.pageY-pageY,D.css("top",currTop),r(0,D.position().top,!1)}),s.bind("mouseup.slimscroll",function(e){d=!1,c(),s.unbind(".slimscroll")}),!1}).bind("selectstart.slimscroll",function(e){return e.stopPropagation(),e.preventDefault(),!1}),R.hover(function(){n()},function(){c()}),D.hover(function(){u=!0},function(){u=!1}),x.hover(function(){h=!0,n(),c()},function(){h=!1,c()}),x.bind("touchstart",function(e,t){e.originalEvent.touches.length&&(g=e.originalEvent.touches[0].pageY)}),x.bind("touchmove",function(e){if(y||e.originalEvent.preventDefault(),e.originalEvent.touches.length){var t=(g-e.originalEvent.touches[0].pageY)/o.touchScrollStep;r(t,!0),g=e.originalEvent.touches[0].pageY}}),l(),"bottom"===o.start?(D.css({top:x.outerHeight()-D.outerHeight()}),r(0,!0)):"top"!==o.start&&(r(e(o.start).position().top,null,!0),o.alwaysVisible||D.hide()),a(this)}}),this}}),e.fn.extend({slimscroll:e.fn.slimScroll})}(jQuery);
510
+
511
+ /* qTip2 v3.0.3 | http://qtip2.com | Released under the MIT and GPL licenses. */
512
+ !function(a,b,c){!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):jQuery&&!jQuery.fn.qtip&&a(jQuery)}(function(d){"use strict";function e(a,b,c,e){this.id=c,this.target=a,this.tooltip=D,this.elements={target:a},this._id=Q+"-"+c,this.timers={img:{}},this.options=b,this.plugins={},this.cache={event:{},target:d(),disabled:C,attr:e,onTooltip:C,lastClass:""},this.rendered=this.destroyed=this.disabled=this.waiting=this.hiddenDuringWait=this.positioning=this.triggering=C}function f(a){return a===D||"object"!==d.type(a)}function g(a){return!(d.isFunction(a)||a&&a.attr||a.length||"object"===d.type(a)&&(a.jquery||a.then))}function h(a){var b,c,e,h;return f(a)?C:(f(a.metadata)&&(a.metadata={type:a.metadata}),"content"in a&&(b=a.content,f(b)||b.jquery||b.done?(c=g(b)?C:b,b=a.content={text:c}):c=b.text,"ajax"in b&&(e=b.ajax,h=e&&e.once!==C,delete b.ajax,b.text=function(a,b){var f=c||d(this).attr(b.options.content.attr)||"Loading...",g=d.ajax(d.extend({},e,{context:b})).then(e.success,D,e.error).then(function(a){return a&&h&&b.set("content.text",a),a},function(a,c,d){b.destroyed||0===a.status||b.set("content.text",c+": "+d)});return h?f:(b.set("content.text",f),g)}),"title"in b&&(d.isPlainObject(b.title)&&(b.button=b.title.button,b.title=b.title.text),g(b.title||C)&&(b.title=C))),"position"in a&&f(a.position)&&(a.position={my:a.position,at:a.position}),"show"in a&&f(a.show)&&(a.show=a.show.jquery?{target:a.show}:a.show===B?{ready:B}:{event:a.show}),"hide"in a&&f(a.hide)&&(a.hide=a.hide.jquery?{target:a.hide}:{event:a.hide}),"style"in a&&f(a.style)&&(a.style={classes:a.style}),d.each(P,function(){this.sanitize&&this.sanitize(a)}),a)}function i(a,b){for(var c,d=0,e=a,f=b.split(".");e=e[f[d++]];)d<f.length&&(c=e);return[c||a,f.pop()]}function j(a,b){var c,d,e;for(c in this.checks)if(this.checks.hasOwnProperty(c))for(d in this.checks[c])this.checks[c].hasOwnProperty(d)&&(e=new RegExp(d,"i").exec(a))&&(b.push(e),("builtin"===c||this.plugins[c])&&this.checks[c][d].apply(this.plugins[c]||this,b))}function k(a){return T.concat("").join(a?"-"+a+" ":" ")}function l(a,b){return b>0?setTimeout(d.proxy(a,this),b):void a.call(this)}function m(a){this.tooltip.hasClass($)||(clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this.timers.show=l.call(this,function(){this.toggle(B,a)},this.options.show.delay))}function n(a){if(!this.tooltip.hasClass($)&&!this.destroyed){var b=d(a.relatedTarget),c=b.closest(U)[0]===this.tooltip[0],e=b[0]===this.options.show.target[0];if(clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this!==b[0]&&"mouse"===this.options.position.target&&c||this.options.hide.fixed&&/mouse(out|leave|move)/.test(a.type)&&(c||e))try{a.preventDefault(),a.stopImmediatePropagation()}catch(f){}else this.timers.hide=l.call(this,function(){this.toggle(C,a)},this.options.hide.delay,this)}}function o(a){!this.tooltip.hasClass($)&&this.options.hide.inactive&&(clearTimeout(this.timers.inactive),this.timers.inactive=l.call(this,function(){this.hide(a)},this.options.hide.inactive))}function p(a){this.rendered&&this.tooltip[0].offsetWidth>0&&this.reposition(a)}function q(a,c,e){d(b.body).delegate(a,(c.split?c:c.join("."+Q+" "))+"."+Q,function(){var a=w.api[d.attr(this,S)];a&&!a.disabled&&e.apply(a,arguments)})}function r(a,c,f){var g,i,j,k,l,m=d(b.body),n=a[0]===b?m:a,o=a.metadata?a.metadata(f.metadata):D,p="html5"===f.metadata.type&&o?o[f.metadata.name]:D,q=a.data(f.metadata.name||"qtipopts");try{q="string"==typeof q?d.parseJSON(q):q}catch(r){}if(k=d.extend(B,{},w.defaults,f,"object"==typeof q?h(q):D,h(p||o)),i=k.position,k.id=c,"boolean"==typeof k.content.text){if(j=a.attr(k.content.attr),k.content.attr===C||!j)return C;k.content.text=j}if(i.container.length||(i.container=m),i.target===C&&(i.target=n),k.show.target===C&&(k.show.target=n),k.show.solo===B&&(k.show.solo=i.container.closest("body")),k.hide.target===C&&(k.hide.target=n),k.position.viewport===B&&(k.position.viewport=i.container),i.container=i.container.eq(0),i.at=new y(i.at,B),i.my=new y(i.my),a.data(Q))if(k.overwrite)a.qtip("destroy",!0);else if(k.overwrite===C)return C;return a.attr(R,c),k.suppress&&(l=a.attr("title"))&&a.removeAttr("title").attr(aa,l).attr("title",""),g=new e(a,k,c,!!j),a.data(Q,g),g}function s(a){return a.charAt(0).toUpperCase()+a.slice(1)}function t(a,b){var d,e,f=b.charAt(0).toUpperCase()+b.slice(1),g=(b+" "+ta.join(f+" ")+f).split(" "),h=0;if(sa[b])return a.css(sa[b]);for(;d=g[h++];)if((e=a.css(d))!==c)return sa[b]=d,e}function u(a,b){return Math.ceil(parseFloat(t(a,b)))}function v(a,b){this._ns="tip",this.options=b,this.offset=b.offset,this.size=[b.width,b.height],this.qtip=a,this.init(a)}var w,x,y,z,A,B=!0,C=!1,D=null,E="x",F="y",G="width",H="height",I="top",J="left",K="bottom",L="right",M="center",N="flipinvert",O="shift",P={},Q="qtip",R="data-hasqtip",S="data-qtip-id",T=["ui-widget","ui-tooltip"],U="."+Q,V="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),W=Q+"-fixed",X=Q+"-default",Y=Q+"-focus",Z=Q+"-hover",$=Q+"-disabled",_="_replacedByqTip",aa="oldtitle",ba={ie:function(){var a,c;for(a=4,c=b.createElement("div");(c.innerHTML="<!--[if gt IE "+a+"]><i></i><![endif]-->")&&c.getElementsByTagName("i")[0];a+=1);return a>4?a:NaN}(),iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||C};x=e.prototype,x._when=function(a){return d.when.apply(d,a)},x.render=function(a){if(this.rendered||this.destroyed)return this;var b=this,c=this.options,e=this.cache,f=this.elements,g=c.content.text,h=c.content.title,i=c.content.button,j=c.position,k=[];return d.attr(this.target[0],"aria-describedby",this._id),e.posClass=this._createPosClass((this.position={my:j.my,at:j.at}).my),this.tooltip=f.tooltip=d("<div/>",{id:this._id,"class":[Q,X,c.style.classes,e.posClass].join(" "),width:c.style.width||"",height:c.style.height||"",tracking:"mouse"===j.target&&j.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":C,"aria-describedby":this._id+"-content","aria-hidden":B}).toggleClass($,this.disabled).attr(S,this.id).data(Q,this).appendTo(j.container).append(f.content=d("<div />",{"class":Q+"-content",id:this._id+"-content","aria-atomic":B})),this.rendered=-1,this.positioning=B,h&&(this._createTitle(),d.isFunction(h)||k.push(this._updateTitle(h,C))),i&&this._createButton(),d.isFunction(g)||k.push(this._updateContent(g,C)),this.rendered=B,this._setWidget(),d.each(P,function(a){var c;"render"===this.initialize&&(c=this(b))&&(b.plugins[a]=c)}),this._unassignEvents(),this._assignEvents(),this._when(k).then(function(){b._trigger("render"),b.positioning=C,b.hiddenDuringWait||!c.show.ready&&!a||b.toggle(B,e.event,C),b.hiddenDuringWait=C}),w.api[this.id]=this,this},x.destroy=function(a){function b(){if(!this.destroyed){this.destroyed=B;var a,b=this.target,c=b.attr(aa);this.rendered&&this.tooltip.stop(1,0).find("*").remove().end().remove(),d.each(this.plugins,function(){this.destroy&&this.destroy()});for(a in this.timers)this.timers.hasOwnProperty(a)&&clearTimeout(this.timers[a]);b.removeData(Q).removeAttr(S).removeAttr(R).removeAttr("aria-describedby"),this.options.suppress&&c&&b.attr("title",c).removeAttr(aa),this._unassignEvents(),this.options=this.elements=this.cache=this.timers=this.plugins=this.mouse=D,delete w.api[this.id]}}return this.destroyed?this.target:(a===B&&"hide"!==this.triggering||!this.rendered?b.call(this):(this.tooltip.one("tooltiphidden",d.proxy(b,this)),!this.triggering&&this.hide()),this.target)},z=x.checks={builtin:{"^id$":function(a,b,c,e){var f=c===B?w.nextid:c,g=Q+"-"+f;f!==C&&f.length>0&&!d("#"+g).length?(this._id=g,this.rendered&&(this.tooltip[0].id=this._id,this.elements.content[0].id=this._id+"-content",this.elements.title[0].id=this._id+"-title")):a[b]=e},"^prerender":function(a,b,c){c&&!this.rendered&&this.render(this.options.show.ready)},"^content.text$":function(a,b,c){this._updateContent(c)},"^content.attr$":function(a,b,c,d){this.options.content.text===this.target.attr(d)&&this._updateContent(this.target.attr(c))},"^content.title$":function(a,b,c){return c?(c&&!this.elements.title&&this._createTitle(),void this._updateTitle(c)):this._removeTitle()},"^content.button$":function(a,b,c){this._updateButton(c)},"^content.title.(text|button)$":function(a,b,c){this.set("content."+b,c)},"^position.(my|at)$":function(a,b,c){"string"==typeof c&&(this.position[b]=a[b]=new y(c,"at"===b))},"^position.container$":function(a,b,c){this.rendered&&this.tooltip.appendTo(c)},"^show.ready$":function(a,b,c){c&&(!this.rendered&&this.render(B)||this.toggle(B))},"^style.classes$":function(a,b,c,d){this.rendered&&this.tooltip.removeClass(d).addClass(c)},"^style.(width|height)":function(a,b,c){this.rendered&&this.tooltip.css(b,c)},"^style.widget|content.title":function(){this.rendered&&this._setWidget()},"^style.def":function(a,b,c){this.rendered&&this.tooltip.toggleClass(X,!!c)},"^events.(render|show|move|hide|focus|blur)$":function(a,b,c){this.rendered&&this.tooltip[(d.isFunction(c)?"":"un")+"bind"]("tooltip"+b,c)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){if(this.rendered){var a=this.options.position;this.tooltip.attr("tracking","mouse"===a.target&&a.adjust.mouse),this._unassignEvents(),this._assignEvents()}}}},x.get=function(a){if(this.destroyed)return this;var b=i(this.options,a.toLowerCase()),c=b[0][b[1]];return c.precedance?c.string():c};var ca=/^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i,da=/^prerender|show\.ready/i;x.set=function(a,b){if(this.destroyed)return this;var c,e=this.rendered,f=C,g=this.options;return"string"==typeof a?(c=a,a={},a[c]=b):a=d.extend({},a),d.each(a,function(b,c){if(e&&da.test(b))return void delete a[b];var h,j=i(g,b.toLowerCase());h=j[0][j[1]],j[0][j[1]]=c&&c.nodeType?d(c):c,f=ca.test(b)||f,a[b]=[j[0],j[1],c,h]}),h(g),this.positioning=B,d.each(a,d.proxy(j,this)),this.positioning=C,this.rendered&&this.tooltip[0].offsetWidth>0&&f&&this.reposition("mouse"===g.position.target?D:this.cache.event),this},x._update=function(a,b){var c=this,e=this.cache;return this.rendered&&a?(d.isFunction(a)&&(a=a.call(this.elements.target,e.event,this)||""),d.isFunction(a.then)?(e.waiting=B,a.then(function(a){return e.waiting=C,c._update(a,b)},D,function(a){return c._update(a,b)})):a===C||!a&&""!==a?C:(a.jquery&&a.length>0?b.empty().append(a.css({display:"block",visibility:"visible"})):b.html(a),this._waitForContent(b).then(function(a){c.rendered&&c.tooltip[0].offsetWidth>0&&c.reposition(e.event,!a.length)}))):C},x._waitForContent=function(a){var b=this.cache;return b.waiting=B,(d.fn.imagesLoaded?a.imagesLoaded():(new d.Deferred).resolve([])).done(function(){b.waiting=C}).promise()},x._updateContent=function(a,b){this._update(a,this.elements.content,b)},x._updateTitle=function(a,b){this._update(a,this.elements.title,b)===C&&this._removeTitle(C)},x._createTitle=function(){var a=this.elements,b=this._id+"-title";a.titlebar&&this._removeTitle(),a.titlebar=d("<div />",{"class":Q+"-titlebar "+(this.options.style.widget?k("header"):"")}).append(a.title=d("<div />",{id:b,"class":Q+"-title","aria-atomic":B})).insertBefore(a.content).delegate(".qtip-close","mousedown keydown mouseup keyup mouseout",function(a){d(this).toggleClass("ui-state-active ui-state-focus","down"===a.type.substr(-4))}).delegate(".qtip-close","mouseover mouseout",function(a){d(this).toggleClass("ui-state-hover","mouseover"===a.type)}),this.options.content.button&&this._createButton()},x._removeTitle=function(a){var b=this.elements;b.title&&(b.titlebar.remove(),b.titlebar=b.title=b.button=D,a!==C&&this.reposition())},x._createPosClass=function(a){return Q+"-pos-"+(a||this.options.position.my).abbrev()},x.reposition=function(c,e){if(!this.rendered||this.positioning||this.destroyed)return this;this.positioning=B;var f,g,h,i,j=this.cache,k=this.tooltip,l=this.options.position,m=l.target,n=l.my,o=l.at,p=l.viewport,q=l.container,r=l.adjust,s=r.method.split(" "),t=k.outerWidth(C),u=k.outerHeight(C),v=0,w=0,x=k.css("position"),y={left:0,top:0},z=k[0].offsetWidth>0,A=c&&"scroll"===c.type,D=d(a),E=q[0].ownerDocument,F=this.mouse;if(d.isArray(m)&&2===m.length)o={x:J,y:I},y={left:m[0],top:m[1]};else if("mouse"===m)o={x:J,y:I},(!r.mouse||this.options.hide.distance)&&j.origin&&j.origin.pageX?c=j.origin:!c||c&&("resize"===c.type||"scroll"===c.type)?c=j.event:F&&F.pageX&&(c=F),"static"!==x&&(y=q.offset()),E.body.offsetWidth!==(a.innerWidth||E.documentElement.clientWidth)&&(g=d(b.body).offset()),y={left:c.pageX-y.left+(g&&g.left||0),top:c.pageY-y.top+(g&&g.top||0)},r.mouse&&A&&F&&(y.left-=(F.scrollX||0)-D.scrollLeft(),y.top-=(F.scrollY||0)-D.scrollTop());else{if("event"===m?c&&c.target&&"scroll"!==c.type&&"resize"!==c.type?j.target=d(c.target):c.target||(j.target=this.elements.target):"event"!==m&&(j.target=d(m.jquery?m:this.elements.target)),m=j.target,m=d(m).eq(0),0===m.length)return this;m[0]===b||m[0]===a?(v=ba.iOS?a.innerWidth:m.width(),w=ba.iOS?a.innerHeight:m.height(),m[0]===a&&(y={top:(p||m).scrollTop(),left:(p||m).scrollLeft()})):P.imagemap&&m.is("area")?f=P.imagemap(this,m,o,P.viewport?s:C):P.svg&&m&&m[0].ownerSVGElement?f=P.svg(this,m,o,P.viewport?s:C):(v=m.outerWidth(C),w=m.outerHeight(C),y=m.offset()),f&&(v=f.width,w=f.height,g=f.offset,y=f.position),y=this.reposition.offset(m,y,q),(ba.iOS>3.1&&ba.iOS<4.1||ba.iOS>=4.3&&ba.iOS<4.33||!ba.iOS&&"fixed"===x)&&(y.left-=D.scrollLeft(),y.top-=D.scrollTop()),(!f||f&&f.adjustable!==C)&&(y.left+=o.x===L?v:o.x===M?v/2:0,y.top+=o.y===K?w:o.y===M?w/2:0)}return y.left+=r.x+(n.x===L?-t:n.x===M?-t/2:0),y.top+=r.y+(n.y===K?-u:n.y===M?-u/2:0),P.viewport?(h=y.adjusted=P.viewport(this,y,l,v,w,t,u),g&&h.left&&(y.left+=g.left),g&&h.top&&(y.top+=g.top),h.my&&(this.position.my=h.my)):y.adjusted={left:0,top:0},j.posClass!==(i=this._createPosClass(this.position.my))&&(j.posClass=i,k.removeClass(j.posClass).addClass(i)),this._trigger("move",[y,p.elem||p],c)?(delete y.adjusted,e===C||!z||isNaN(y.left)||isNaN(y.top)||"mouse"===m||!d.isFunction(l.effect)?k.css(y):d.isFunction(l.effect)&&(l.effect.call(k,this,d.extend({},y)),k.queue(function(a){d(this).css({opacity:"",height:""}),ba.ie&&this.style.removeAttribute("filter"),a()})),this.positioning=C,this):this},x.reposition.offset=function(a,c,e){function f(a,b){c.left+=b*a.scrollLeft(),c.top+=b*a.scrollTop()}if(!e[0])return c;var g,h,i,j,k=d(a[0].ownerDocument),l=!!ba.ie&&"CSS1Compat"!==b.compatMode,m=e[0];do"static"!==(h=d.css(m,"position"))&&("fixed"===h?(i=m.getBoundingClientRect(),f(k,-1)):(i=d(m).position(),i.left+=parseFloat(d.css(m,"borderLeftWidth"))||0,i.top+=parseFloat(d.css(m,"borderTopWidth"))||0),c.left-=i.left+(parseFloat(d.css(m,"marginLeft"))||0),c.top-=i.top+(parseFloat(d.css(m,"marginTop"))||0),g||"hidden"===(j=d.css(m,"overflow"))||"visible"===j||(g=d(m)));while(m=m.offsetParent);return g&&(g[0]!==k[0]||l)&&f(g,1),c};var ea=(y=x.reposition.Corner=function(a,b){a=(""+a).replace(/([A-Z])/," $1").replace(/middle/gi,M).toLowerCase(),this.x=(a.match(/left|right/i)||a.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(a.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase(),this.forceY=!!b;var c=a.charAt(0);this.precedance="t"===c||"b"===c?F:E}).prototype;ea.invert=function(a,b){this[a]=this[a]===J?L:this[a]===L?J:b||this[a]},ea.string=function(a){var b=this.x,c=this.y,d=b!==c?"center"===b||"center"!==c&&(this.precedance===F||this.forceY)?[c,b]:[b,c]:[b];return a!==!1?d.join(" "):d},ea.abbrev=function(){var a=this.string(!1);return a[0].charAt(0)+(a[1]&&a[1].charAt(0)||"")},ea.clone=function(){return new y(this.string(),this.forceY)},x.toggle=function(a,c){var e=this.cache,f=this.options,g=this.tooltip;if(c){if(/over|enter/.test(c.type)&&e.event&&/out|leave/.test(e.event.type)&&f.show.target.add(c.target).length===f.show.target.length&&g.has(c.relatedTarget).length)return this;e.event=d.event.fix(c)}if(this.waiting&&!a&&(this.hiddenDuringWait=B),!this.rendered)return a?this.render(1):this;if(this.destroyed||this.disabled)return this;var h,i,j,k=a?"show":"hide",l=this.options[k],m=this.options.position,n=this.options.content,o=this.tooltip.css("width"),p=this.tooltip.is(":visible"),q=a||1===l.target.length,r=!c||l.target.length<2||e.target[0]===c.target;return(typeof a).search("boolean|number")&&(a=!p),h=!g.is(":animated")&&p===a&&r,i=h?D:!!this._trigger(k,[90]),this.destroyed?this:(i!==C&&a&&this.focus(c),!i||h?this:(d.attr(g[0],"aria-hidden",!a),a?(this.mouse&&(e.origin=d.event.fix(this.mouse)),d.isFunction(n.text)&&this._updateContent(n.text,C),d.isFunction(n.title)&&this._updateTitle(n.title,C),!A&&"mouse"===m.target&&m.adjust.mouse&&(d(b).bind("mousemove."+Q,this._storeMouse),A=B),o||g.css("width",g.outerWidth(C)),this.reposition(c,arguments[2]),o||g.css("width",""),l.solo&&("string"==typeof l.solo?d(l.solo):d(U,l.solo)).not(g).not(l.target).qtip("hide",new d.Event("tooltipsolo"))):(clearTimeout(this.timers.show),delete e.origin,A&&!d(U+'[tracking="true"]:visible',l.solo).not(g).length&&(d(b).unbind("mousemove."+Q),A=C),this.blur(c)),j=d.proxy(function(){a?(ba.ie&&g[0].style.removeAttribute("filter"),g.css("overflow",""),"string"==typeof l.autofocus&&d(this.options.show.autofocus,g).focus(),this.options.show.target.trigger("qtip-"+this.id+"-inactive")):g.css({display:"",visibility:"",opacity:"",left:"",top:""}),this._trigger(a?"visible":"hidden")},this),l.effect===C||q===C?(g[k](),j()):d.isFunction(l.effect)?(g.stop(1,1),l.effect.call(g,this),g.queue("fx",function(a){j(),a()})):g.fadeTo(90,a?1:0,j),a&&l.target.trigger("qtip-"+this.id+"-inactive"),this))},x.show=function(a){return this.toggle(B,a)},x.hide=function(a){return this.toggle(C,a)},x.focus=function(a){if(!this.rendered||this.destroyed)return this;var b=d(U),c=this.tooltip,e=parseInt(c[0].style.zIndex,10),f=w.zindex+b.length;return c.hasClass(Y)||this._trigger("focus",[f],a)&&(e!==f&&(b.each(function(){this.style.zIndex>e&&(this.style.zIndex=this.style.zIndex-1)}),b.filter("."+Y).qtip("blur",a)),c.addClass(Y)[0].style.zIndex=f),this},x.blur=function(a){return!this.rendered||this.destroyed?this:(this.tooltip.removeClass(Y),this._trigger("blur",[this.tooltip.css("zIndex")],a),this)},x.disable=function(a){return this.destroyed?this:("toggle"===a?a=!(this.rendered?this.tooltip.hasClass($):this.disabled):"boolean"!=typeof a&&(a=B),this.rendered&&this.tooltip.toggleClass($,a).attr("aria-disabled",a),this.disabled=!!a,this)},x.enable=function(){return this.disable(C)},x._createButton=function(){var a=this,b=this.elements,c=b.tooltip,e=this.options.content.button,f="string"==typeof e,g=f?e:"Close tooltip";b.button&&b.button.remove(),e.jquery?b.button=e:b.button=d("<a />",{"class":"qtip-close "+(this.options.style.widget?"":Q+"-icon"),title:g,"aria-label":g}).prepend(d("<span />",{"class":"ui-icon ui-icon-close",html:"&times;"})),b.button.appendTo(b.titlebar||c).attr("role","button").click(function(b){return c.hasClass($)||a.hide(b),C})},x._updateButton=function(a){if(!this.rendered)return C;var b=this.elements.button;a?this._createButton():b.remove()},x._setWidget=function(){var a=this.options.style.widget,b=this.elements,c=b.tooltip,d=c.hasClass($);c.removeClass($),$=a?"ui-state-disabled":"qtip-disabled",c.toggleClass($,d),c.toggleClass("ui-helper-reset "+k(),a).toggleClass(X,this.options.style.def&&!a),b.content&&b.content.toggleClass(k("content"),a),b.titlebar&&b.titlebar.toggleClass(k("header"),a),b.button&&b.button.toggleClass(Q+"-icon",!a)},x._storeMouse=function(a){return(this.mouse=d.event.fix(a)).type="mousemove",this},x._bind=function(a,b,c,e,f){if(a&&c&&b.length){var g="."+this._id+(e?"-"+e:"");return d(a).bind((b.split?b:b.join(g+" "))+g,d.proxy(c,f||this)),this}},x._unbind=function(a,b){return a&&d(a).unbind("."+this._id+(b?"-"+b:"")),this},x._trigger=function(a,b,c){var e=new d.Event("tooltip"+a);return e.originalEvent=c&&d.extend({},c)||this.cache.event||D,this.triggering=a,this.tooltip.trigger(e,[this].concat(b||[])),this.triggering=C,!e.isDefaultPrevented()},x._bindEvents=function(a,b,c,e,f,g){var h=c.filter(e).add(e.filter(c)),i=[];h.length&&(d.each(b,function(b,c){var e=d.inArray(c,a);e>-1&&i.push(a.splice(e,1)[0])}),i.length&&(this._bind(h,i,function(a){var b=this.rendered?this.tooltip[0].offsetWidth>0:!1;(b?g:f).call(this,a)}),c=c.not(h),e=e.not(h))),this._bind(c,a,f),this._bind(e,b,g)},x._assignInitialEvents=function(a){function b(a){return this.disabled||this.destroyed?C:(this.cache.event=a&&d.event.fix(a),this.cache.target=a&&d(a.target),clearTimeout(this.timers.show),void(this.timers.show=l.call(this,function(){this.render("object"==typeof a||c.show.ready)},c.prerender?0:c.show.delay)))}var c=this.options,e=c.show.target,f=c.hide.target,g=c.show.event?d.trim(""+c.show.event).split(" "):[],h=c.hide.event?d.trim(""+c.hide.event).split(" "):[];this._bind(this.elements.target,["remove","removeqtip"],function(){this.destroy(!0)},"destroy"),/mouse(over|enter)/i.test(c.show.event)&&!/mouse(out|leave)/i.test(c.hide.event)&&h.push("mouseleave"),this._bind(e,"mousemove",function(a){this._storeMouse(a),this.cache.onTarget=B}),this._bindEvents(g,h,e,f,b,function(){return this.timers?void clearTimeout(this.timers.show):C}),(c.show.ready||c.prerender)&&b.call(this,a)},x._assignEvents=function(){var c=this,e=this.options,f=e.position,g=this.tooltip,h=e.show.target,i=e.hide.target,j=f.container,k=f.viewport,l=d(b),q=d(a),r=e.show.event?d.trim(""+e.show.event).split(" "):[],s=e.hide.event?d.trim(""+e.hide.event).split(" "):[];d.each(e.events,function(a,b){c._bind(g,"toggle"===a?["tooltipshow","tooltiphide"]:["tooltip"+a],b,null,g)}),/mouse(out|leave)/i.test(e.hide.event)&&"window"===e.hide.leave&&this._bind(l,["mouseout","blur"],function(a){/select|option/.test(a.target.nodeName)||a.relatedTarget||this.hide(a)}),e.hide.fixed?i=i.add(g.addClass(W)):/mouse(over|enter)/i.test(e.show.event)&&this._bind(i,"mouseleave",function(){clearTimeout(this.timers.show)}),(""+e.hide.event).indexOf("unfocus")>-1&&this._bind(j.closest("html"),["mousedown","touchstart"],function(a){var b=d(a.target),c=this.rendered&&!this.tooltip.hasClass($)&&this.tooltip[0].offsetWidth>0,e=b.parents(U).filter(this.tooltip[0]).length>0;b[0]===this.target[0]||b[0]===this.tooltip[0]||e||this.target.has(b[0]).length||!c||this.hide(a)}),"number"==typeof e.hide.inactive&&(this._bind(h,"qtip-"+this.id+"-inactive",o,"inactive"),this._bind(i.add(g),w.inactiveEvents,o)),this._bindEvents(r,s,h,i,m,n),this._bind(h.add(g),"mousemove",function(a){if("number"==typeof e.hide.distance){var b=this.cache.origin||{},c=this.options.hide.distance,d=Math.abs;(d(a.pageX-b.pageX)>=c||d(a.pageY-b.pageY)>=c)&&this.hide(a)}this._storeMouse(a)}),"mouse"===f.target&&f.adjust.mouse&&(e.hide.event&&this._bind(h,["mouseenter","mouseleave"],function(a){return this.cache?void(this.cache.onTarget="mouseenter"===a.type):C}),this._bind(l,"mousemove",function(a){this.rendered&&this.cache.onTarget&&!this.tooltip.hasClass($)&&this.tooltip[0].offsetWidth>0&&this.reposition(a)})),(f.adjust.resize||k.length)&&this._bind(d.event.special.resize?k:q,"resize",p),f.adjust.scroll&&this._bind(q.add(f.container),"scroll",p)},x._unassignEvents=function(){var c=this.options,e=c.show.target,f=c.hide.target,g=d.grep([this.elements.target[0],this.rendered&&this.tooltip[0],c.position.container[0],c.position.viewport[0],c.position.container.closest("html")[0],a,b],function(a){return"object"==typeof a});e&&e.toArray&&(g=g.concat(e.toArray())),f&&f.toArray&&(g=g.concat(f.toArray())),this._unbind(g)._unbind(g,"destroy")._unbind(g,"inactive")},d(function(){q(U,["mouseenter","mouseleave"],function(a){var b="mouseenter"===a.type,c=d(a.currentTarget),e=d(a.relatedTarget||a.target),f=this.options;b?(this.focus(a),c.hasClass(W)&&!c.hasClass($)&&clearTimeout(this.timers.hide)):"mouse"===f.position.target&&f.position.adjust.mouse&&f.hide.event&&f.show.target&&!e.closest(f.show.target[0]).length&&this.hide(a),c.toggleClass(Z,b)}),q("["+S+"]",V,o)}),w=d.fn.qtip=function(a,b,e){var f=(""+a).toLowerCase(),g=D,i=d.makeArray(arguments).slice(1),j=i[i.length-1],k=this[0]?d.data(this[0],Q):D;return!arguments.length&&k||"api"===f?k:"string"==typeof a?(this.each(function(){var a=d.data(this,Q);if(!a)return B;if(j&&j.timeStamp&&(a.cache.event=j),!b||"option"!==f&&"options"!==f)a[f]&&a[f].apply(a,i);else{if(e===c&&!d.isPlainObject(b))return g=a.get(b),C;a.set(b,e)}}),g!==D?g:this):"object"!=typeof a&&arguments.length?void 0:(k=h(d.extend(B,{},a)),this.each(function(a){var b,c;return c=d.isArray(k.id)?k.id[a]:k.id,c=!c||c===C||c.length<1||w.api[c]?w.nextid++:c,b=r(d(this),c,k),b===C?B:(w.api[c]=b,d.each(P,function(){"initialize"===this.initialize&&this(b)}),void b._assignInitialEvents(j))}))},d.qtip=e,w.api={},d.each({attr:function(a,b){if(this.length){var c=this[0],e="title",f=d.data(c,"qtip");if(a===e&&f&&f.options&&"object"==typeof f&&"object"==typeof f.options&&f.options.suppress)return arguments.length<2?d.attr(c,aa):(f&&f.options.content.attr===e&&f.cache.attr&&f.set("content.text",b),this.attr(aa,b))}return d.fn["attr"+_].apply(this,arguments)},clone:function(a){var b=d.fn["clone"+_].apply(this,arguments);return a||b.filter("["+aa+"]").attr("title",function(){return d.attr(this,aa)}).removeAttr(aa),b}},function(a,b){if(!b||d.fn[a+_])return B;var c=d.fn[a+_]=d.fn[a];d.fn[a]=function(){return b.apply(this,arguments)||c.apply(this,arguments)}}),d.ui||(d["cleanData"+_]=d.cleanData,d.cleanData=function(a){for(var b,c=0;(b=d(a[c])).length;c++)if(b.attr(R))try{b.triggerHandler("removeqtip")}catch(e){}d["cleanData"+_].apply(this,arguments)}),w.version="3.0.3",w.nextid=0,w.inactiveEvents=V,w.zindex=15e3,w.defaults={prerender:C,id:C,overwrite:B,suppress:B,content:{text:B,attr:"title",title:C,button:C},position:{my:"top left",at:"bottom right",target:C,container:C,viewport:C,adjust:{x:0,y:0,mouse:B,scroll:B,resize:B,method:"flipinvert flipinvert"},effect:function(a,b){d(this).animate(b,{duration:200,queue:C})}},show:{target:C,event:"mouseenter",effect:B,delay:90,solo:C,ready:C,autofocus:C},hide:{target:C,event:"mouseleave",effect:B,delay:0,fixed:C,inactive:C,leave:"window",distance:C},style:{classes:"",widget:C,width:C,height:C,def:B},events:{render:D,move:D,show:D,hide:D,toggle:D,visible:D,hidden:D,focus:D,blur:D}};var fa,ga,ha,ia,ja,ka="margin",la="border",ma="color",na="background-color",oa="transparent",pa=" !important",qa=!!b.createElement("canvas").getContext,ra=/rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,sa={},ta=["Webkit","O","Moz","ms"];qa?(ia=a.devicePixelRatio||1,ja=function(){var a=b.createElement("canvas").getContext("2d");return a.backingStorePixelRatio||a.webkitBackingStorePixelRatio||a.mozBackingStorePixelRatio||a.msBackingStorePixelRatio||a.oBackingStorePixelRatio||1}(),ha=ia/ja):ga=function(a,b,c){return"<qtipvml:"+a+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(b||"")+' style="behavior: url(#default#VML); '+(c||"")+'" />'},d.extend(v.prototype,{init:function(a){var b,c;c=this.element=a.elements.tip=d("<div />",{"class":Q+"-tip"}).prependTo(a.tooltip),qa?(b=d("<canvas />").appendTo(this.element)[0].getContext("2d"),b.lineJoin="miter",b.miterLimit=1e5,b.save()):(b=ga("shape",'coordorigin="0,0"',"position:absolute;"),this.element.html(b+b),a._bind(d("*",c).add(c),["click","mousedown"],function(a){a.stopPropagation()},this._ns)),a._bind(a.tooltip,"tooltipmove",this.reposition,this._ns,this),this.create()},_swapDimensions:function(){this.size[0]=this.options.height,this.size[1]=this.options.width},_resetDimensions:function(){this.size[0]=this.options.width,this.size[1]=this.options.height},_useTitle:function(a){var b=this.qtip.elements.titlebar;return b&&(a.y===I||a.y===M&&this.element.position().top+this.size[1]/2+this.options.offset<b.outerHeight(B))},_parseCorner:function(a){var b=this.qtip.options.position.my;return a===C||b===C?a=C:a===B?a=new y(b.string()):a.string||(a=new y(a),a.fixed=B),a},_parseWidth:function(a,b,c){var d=this.qtip.elements,e=la+s(b)+"Width";return(c?u(c,e):u(d.content,e)||u(this._useTitle(a)&&d.titlebar||d.content,e)||u(d.tooltip,e))||0},_parseRadius:function(a){var b=this.qtip.elements,c=la+s(a.y)+s(a.x)+"Radius";return ba.ie<9?0:u(this._useTitle(a)&&b.titlebar||b.content,c)||u(b.tooltip,c)||0},_invalidColour:function(a,b,c){var d=a.css(b);return!d||c&&d===a.css(c)||ra.test(d)?C:d},_parseColours:function(a){var b=this.qtip.elements,c=this.element.css("cssText",""),e=la+s(a[a.precedance])+s(ma),f=this._useTitle(a)&&b.titlebar||b.content,g=this._invalidColour,h=[];return h[0]=g(c,na)||g(f,na)||g(b.content,na)||g(b.tooltip,na)||c.css(na),h[1]=g(c,e,ma)||g(f,e,ma)||g(b.content,e,ma)||g(b.tooltip,e,ma)||b.tooltip.css(e),d("*",c).add(c).css("cssText",na+":"+oa+pa+";"+la+":0"+pa+";"),h},_calculateSize:function(a){var b,c,d,e=a.precedance===F,f=this.options.width,g=this.options.height,h="c"===a.abbrev(),i=(e?f:g)*(h?.5:1),j=Math.pow,k=Math.round,l=Math.sqrt(j(i,2)+j(g,2)),m=[this.border/i*l,this.border/g*l];return m[2]=Math.sqrt(j(m[0],2)-j(this.border,2)),m[3]=Math.sqrt(j(m[1],2)-j(this.border,2)),b=l+m[2]+m[3]+(h?0:m[0]),c=b/l,d=[k(c*f),k(c*g)],e?d:d.reverse()},_calculateTip:function(a,b,c){c=c||1,b=b||this.size;var d=b[0]*c,e=b[1]*c,f=Math.ceil(d/2),g=Math.ceil(e/2),h={br:[0,0,d,e,d,0],bl:[0,0,d,0,0,e],tr:[0,e,d,0,d,e],tl:[0,0,0,e,d,e],tc:[0,e,f,0,d,e],bc:[0,0,d,0,f,e],rc:[0,0,d,g,0,e],lc:[d,0,d,e,0,g]};return h.lt=h.br,h.rt=h.bl,h.lb=h.tr,h.rb=h.tl,h[a.abbrev()]},_drawCoords:function(a,b){a.beginPath(),a.moveTo(b[0],b[1]),a.lineTo(b[2],b[3]),a.lineTo(b[4],b[5]),a.closePath()},create:function(){var a=this.corner=(qa||ba.ie)&&this._parseCorner(this.options.corner);return this.enabled=!!this.corner&&"c"!==this.corner.abbrev(),this.enabled&&(this.qtip.cache.corner=a.clone(),this.update()),this.element.toggle(this.enabled),this.corner},update:function(b,c){if(!this.enabled)return this;var e,f,g,h,i,j,k,l,m=this.qtip.elements,n=this.element,o=n.children(),p=this.options,q=this.size,r=p.mimic,s=Math.round;b||(b=this.qtip.cache.corner||this.corner),r===C?r=b:(r=new y(r),r.precedance=b.precedance,"inherit"===r.x?r.x=b.x:"inherit"===r.y?r.y=b.y:r.x===r.y&&(r[b.precedance]=b[b.precedance])),f=r.precedance,b.precedance===E?this._swapDimensions():this._resetDimensions(),e=this.color=this._parseColours(b),e[1]!==oa?(l=this.border=this._parseWidth(b,b[b.precedance]),p.border&&1>l&&!ra.test(e[1])&&(e[0]=e[1]),this.border=l=p.border!==B?p.border:l):this.border=l=0,k=this.size=this._calculateSize(b),n.css({width:k[0],height:k[1],lineHeight:k[1]+"px"}),j=b.precedance===F?[s(r.x===J?l:r.x===L?k[0]-q[0]-l:(k[0]-q[0])/2),s(r.y===I?k[1]-q[1]:0)]:[s(r.x===J?k[0]-q[0]:0),s(r.y===I?l:r.y===K?k[1]-q[1]-l:(k[1]-q[1])/2)],qa?(g=o[0].getContext("2d"),g.restore(),g.save(),g.clearRect(0,0,6e3,6e3),h=this._calculateTip(r,q,ha),i=this._calculateTip(r,this.size,ha),o.attr(G,k[0]*ha).attr(H,k[1]*ha),o.css(G,k[0]).css(H,k[1]),this._drawCoords(g,i),g.fillStyle=e[1],g.fill(),g.translate(j[0]*ha,j[1]*ha),this._drawCoords(g,h),g.fillStyle=e[0],g.fill()):(h=this._calculateTip(r),h="m"+h[0]+","+h[1]+" l"+h[2]+","+h[3]+" "+h[4]+","+h[5]+" xe",j[2]=l&&/^(r|b)/i.test(b.string())?8===ba.ie?2:1:0,o.css({coordsize:k[0]+l+" "+k[1]+l,antialias:""+(r.string().indexOf(M)>-1),left:j[0]-j[2]*Number(f===E),top:j[1]-j[2]*Number(f===F),width:k[0]+l,height:k[1]+l}).each(function(a){var b=d(this);b[b.prop?"prop":"attr"]({coordsize:k[0]+l+" "+k[1]+l,path:h,fillcolor:e[0],filled:!!a,stroked:!a}).toggle(!(!l&&!a)),!a&&b.html(ga("stroke",'weight="'+2*l+'px" color="'+e[1]+'" miterlimit="1000" joinstyle="miter"'))})),a.opera&&setTimeout(function(){m.tip.css({display:"inline-block",visibility:"visible"})},1),c!==C&&this.calculate(b,k)},calculate:function(a,b){if(!this.enabled)return C;var c,e,f=this,g=this.qtip.elements,h=this.element,i=this.options.offset,j={};return a=a||this.corner,c=a.precedance,b=b||this._calculateSize(a),e=[a.x,a.y],c===E&&e.reverse(),d.each(e,function(d,e){var h,k,l;e===M?(h=c===F?J:I,j[h]="50%",j[ka+"-"+h]=-Math.round(b[c===F?0:1]/2)+i):(h=f._parseWidth(a,e,g.tooltip),k=f._parseWidth(a,e,g.content),l=f._parseRadius(a),j[e]=Math.max(-f.border,d?k:i+(l>h?l:-h)))}),j[a[c]]-=b[c===E?0:1],h.css({margin:"",top:"",bottom:"",left:"",right:""}).css(j),j},reposition:function(a,b,d){function e(a,b,c,d,e){a===O&&j.precedance===b&&k[d]&&j[c]!==M?j.precedance=j.precedance===E?F:E:a!==O&&k[d]&&(j[b]=j[b]===M?k[d]>0?d:e:j[b]===d?e:d)}function f(a,b,e){j[a]===M?p[ka+"-"+b]=o[a]=g[ka+"-"+b]-k[b]:(h=g[e]!==c?[k[b],-g[b]]:[-k[b],g[b]],(o[a]=Math.max(h[0],h[1]))>h[0]&&(d[b]-=k[b],o[b]=C),p[g[e]!==c?e:b]=o[a])}if(this.enabled){var g,h,i=b.cache,j=this.corner.clone(),k=d.adjusted,l=b.options.position.adjust.method.split(" "),m=l[0],n=l[1]||l[0],o={left:C,top:C,x:0,y:0},p={};this.corner.fixed!==B&&(e(m,E,F,J,L),e(n,F,E,I,K),j.string()===i.corner.string()&&i.cornerTop===k.top&&i.cornerLeft===k.left||this.update(j,C)),g=this.calculate(j),g.right!==c&&(g.left=-g.right),g.bottom!==c&&(g.top=-g.bottom),g.user=this.offset,o.left=m===O&&!!k.left,o.left&&f(E,J,L),o.top=n===O&&!!k.top,o.top&&f(F,I,K),this.element.css(p).toggle(!(o.x&&o.y||j.x===M&&o.y||j.y===M&&o.x)),d.left-=g.left.charAt?g.user:m!==O||o.top||!o.left&&!o.top?g.left+this.border:0,d.top-=g.top.charAt?g.user:n!==O||o.left||!o.left&&!o.top?g.top+this.border:0,i.cornerLeft=k.left,i.cornerTop=k.top,i.corner=j.clone()}},destroy:function(){this.qtip._unbind(this.qtip.tooltip,this._ns),this.qtip.elements.tip&&this.qtip.elements.tip.find("*").remove().end().remove()}}),fa=P.tip=function(a){return new v(a,a.options.style.tip)},fa.initialize="render",fa.sanitize=function(a){if(a.style&&"tip"in a.style){var b=a.style.tip;"object"!=typeof b&&(b=a.style.tip={corner:b}),/string|boolean/i.test(typeof b.corner)||(b.corner=B)}},z.tip={"^position.my|style.tip.(corner|mimic|border)$":function(){this.create(),this.qtip.reposition()},"^style.tip.(height|width)$":function(a){this.size=[a.width,a.height],this.update(),this.qtip.reposition()},"^content.title|style.(classes|widget)$":function(){this.update()}},d.extend(B,w.defaults,{style:{tip:{corner:B,mimic:C,width:6,height:6,border:B,offset:0}}}),P.viewport=function(c,d,e,f,g,h,i){function j(a,b,c,e,f,g,h,i,j){var k=d[f],s=u[a],t=v[a],w=c===O,x=s===f?j:s===g?-j:-j/2,y=t===f?i:t===g?-i:-i/2,z=q[f]+r[f]-(n?0:m[f]),A=z-k,B=k+j-(h===G?o:p)-z,C=x-(u.precedance===a||s===u[b]?y:0)-(t===M?i/2:0);return w?(C=(s===f?1:-1)*x,d[f]+=A>0?A:B>0?-B:0,d[f]=Math.max(-m[f]+r[f],k-C,Math.min(Math.max(-m[f]+r[f]+(h===G?o:p),k+C),d[f],"center"===s?k-x:1e9))):(e*=c===N?2:0,A>0&&(s!==f||B>0)?(d[f]-=C+e,l.invert(a,f)):B>0&&(s!==g||A>0)&&(d[f]-=(s===M?-C:C)+e,l.invert(a,g)),d[f]<q[f]&&-d[f]>B&&(d[f]=k,l=u.clone())),d[f]-k}var k,l,m,n,o,p,q,r,s=e.target,t=c.elements.tooltip,u=e.my,v=e.at,w=e.adjust,x=w.method.split(" "),y=x[0],z=x[1]||x[0],A=e.viewport,B=e.container,D={left:0,top:0};return A.jquery&&s[0]!==a&&s[0]!==b.body&&"none"!==w.method?(m=B.offset()||D,n="static"===B.css("position"),k="fixed"===t.css("position"),o=A[0]===a?A.width():A.outerWidth(C),p=A[0]===a?A.height():A.outerHeight(C),q={left:k?0:A.scrollLeft(),top:k?0:A.scrollTop()},r=A.offset()||D,"shift"===y&&"shift"===z||(l=u.clone()),D={left:"none"!==y?j(E,F,y,w.x,J,L,G,f,h):0,top:"none"!==z?j(F,E,z,w.y,I,K,H,g,i):0,my:l}):D}})}(window,document);
513
+
514
+ /* bootstrap-switch v3.3.4 | http://bootstrapswitch.com/ | Copyright (c) 2012-2017 Mattia Larentis & Emanuele Marchi. Released under the Apache 2.0 license. */
515
+ (function(a,b){if('function'==typeof define&&define.amd)define(['jquery'],b);else if('undefined'!=typeof exports)b(require('jquery'));else{b(a.jquery),a.bootstrapSwitch={exports:{}}.exports}})(this,function(a){'use strict';function c(j,k){if(!(j instanceof k))throw new TypeError('Cannot call a class as a function')}var d=function(j){return j&&j.__esModule?j:{default:j}}(a),e=Object.assign||function(j){for(var l,k=1;k<arguments.length;k++)for(var m in l=arguments[k],l)Object.prototype.hasOwnProperty.call(l,m)&&(j[m]=l[m]);return j},f=function(){function j(k,l){for(var n,m=0;m<l.length;m++)n=l[m],n.enumerable=n.enumerable||!1,n.configurable=!0,'value'in n&&(n.writable=!0),Object.defineProperty(k,n.key,n)}return function(k,l,m){return l&&j(k.prototype,l),m&&j(k,m),k}}(),g=d.default||window.jQuery||window.$,h=function(){function j(k){var l=this,m=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};c(this,j),this.$element=g(k),this.options=g.extend({},g.fn.bootstrapSwitch.defaults,this._getElementOptions(),m),this.prevOptions={},this.$wrapper=g('<div>',{class:function(){var o=[];return o.push(l.options.state?'on':'off'),l.options.size&&o.push(l.options.size),l.options.disabled&&o.push('disabled'),l.options.readonly&&o.push('readonly'),l.options.indeterminate&&o.push('indeterminate'),l.options.inverse&&o.push('inverse'),l.$element.attr('id')&&o.push('id-'+l.$element.attr('id')),o.map(l._getClass.bind(l)).concat([l.options.baseClass],l._getClasses(l.options.wrapperClass)).join(' ')}}),this.$container=g('<div>',{class:this._getClass('container')}),this.$on=g('<span>',{html:this.options.onText,class:this._getClass('handle-on')+' '+this._getClass(this.options.onColor)}),this.$off=g('<span>',{html:this.options.offText,class:this._getClass('handle-off')+' '+this._getClass(this.options.offColor)}),this.$label=g('<span>',{html:this.options.labelText,class:this._getClass('label')}),this.$element.on('init.bootstrapSwitch',this.options.onInit.bind(this,k)),this.$element.on('switchChange.bootstrapSwitch',function(){for(var n=arguments.length,o=Array(n),p=0;p<n;p++)o[p]=arguments[p];!1===l.options.onSwitchChange.apply(k,o)&&(l.$element.is(':radio')?g('[name="'+l.$element.attr('name')+'"]').trigger('previousState.bootstrapSwitch',!0):l.$element.trigger('previousState.bootstrapSwitch',!0))}),this.$container=this.$element.wrap(this.$container).parent(),this.$wrapper=this.$container.wrap(this.$wrapper).parent(),this.$element.before(this.options.inverse?this.$off:this.$on).before(this.$label).before(this.options.inverse?this.$on:this.$off),this.options.indeterminate&&this.$element.prop('indeterminate',!0),this._init(),this._elementHandlers(),this._handleHandlers(),this._labelHandlers(),this._formHandler(),this._externalLabelHandler(),this.$element.trigger('init.bootstrapSwitch',this.options.state)}return f(j,[{key:'setPrevOptions',value:function(){this.prevOptions=e({},this.options)}},{key:'state',value:function(l,m){return'undefined'==typeof l?this.options.state:this.options.disabled||this.options.readonly||this.options.state&&!this.options.radioAllOff&&this.$element.is(':radio')?this.$element:(this.$element.is(':radio')?g('[name="'+this.$element.attr('name')+'"]').trigger('setPreviousOptions.bootstrapSwitch'):this.$element.trigger('setPreviousOptions.bootstrapSwitch'),this.options.indeterminate&&this.indeterminate(!1),this.$element.prop('checked',!!l).trigger('change.bootstrapSwitch',m),this.$element)}},{key:'toggleState',value:function(l){return this.options.disabled||this.options.readonly?this.$element:this.options.indeterminate?(this.indeterminate(!1),this.state(!0)):this.$element.prop('checked',!this.options.state).trigger('change.bootstrapSwitch',l)}},{key:'size',value:function(l){return'undefined'==typeof l?this.options.size:(null!=this.options.size&&this.$wrapper.removeClass(this._getClass(this.options.size)),l&&this.$wrapper.addClass(this._getClass(l)),this._width(),this._containerPosition(),this.options.size=l,this.$element)}},{key:'animate',value:function(l){return'undefined'==typeof l?this.options.animate:this.options.animate===!!l?this.$element:this.toggleAnimate()}},{key:'toggleAnimate',value:function(){return this.options.animate=!this.options.animate,this.$wrapper.toggleClass(this._getClass('animate')),this.$element}},{key:'disabled',value:function(l){return'undefined'==typeof l?this.options.disabled:this.options.disabled===!!l?this.$element:this.toggleDisabled()}},{key:'toggleDisabled',value:function(){return this.options.disabled=!this.options.disabled,this.$element.prop('disabled',this.options.disabled),this.$wrapper.toggleClass(this._getClass('disabled')),this.$element}},{key:'readonly',value:function(l){return'undefined'==typeof l?this.options.readonly:this.options.readonly===!!l?this.$element:this.toggleReadonly()}},{key:'toggleReadonly',value:function(){return this.options.readonly=!this.options.readonly,this.$element.prop('readonly',this.options.readonly),this.$wrapper.toggleClass(this._getClass('readonly')),this.$element}},{key:'indeterminate',value:function(l){return'undefined'==typeof l?this.options.indeterminate:this.options.indeterminate===!!l?this.$element:this.toggleIndeterminate()}},{key:'toggleIndeterminate',value:function(){return this.options.indeterminate=!this.options.indeterminate,this.$element.prop('indeterminate',this.options.indeterminate),this.$wrapper.toggleClass(this._getClass('indeterminate')),this._containerPosition(),this.$element}},{key:'inverse',value:function(l){return'undefined'==typeof l?this.options.inverse:this.options.inverse===!!l?this.$element:this.toggleInverse()}},{key:'toggleInverse',value:function(){this.$wrapper.toggleClass(this._getClass('inverse'));var l=this.$on.clone(!0),m=this.$off.clone(!0);return this.$on.replaceWith(m),this.$off.replaceWith(l),this.$on=m,this.$off=l,this.options.inverse=!this.options.inverse,this.$element}},{key:'onColor',value:function(l){return'undefined'==typeof l?this.options.onColor:(this.options.onColor&&this.$on.removeClass(this._getClass(this.options.onColor)),this.$on.addClass(this._getClass(l)),this.options.onColor=l,this.$element)}},{key:'offColor',value:function(l){return'undefined'==typeof l?this.options.offColor:(this.options.offColor&&this.$off.removeClass(this._getClass(this.options.offColor)),this.$off.addClass(this._getClass(l)),this.options.offColor=l,this.$element)}},{key:'onText',value:function(l){return'undefined'==typeof l?this.options.onText:(this.$on.html(l),this._width(),this._containerPosition(),this.options.onText=l,this.$element)}},{key:'offText',value:function(l){return'undefined'==typeof l?this.options.offText:(this.$off.html(l),this._width(),this._containerPosition(),this.options.offText=l,this.$element)}},{key:'labelText',value:function(l){return'undefined'==typeof l?this.options.labelText:(this.$label.html(l),this._width(),this.options.labelText=l,this.$element)}},{key:'handleWidth',value:function(l){return'undefined'==typeof l?this.options.handleWidth:(this.options.handleWidth=l,this._width(),this._containerPosition(),this.$element)}},{key:'labelWidth',value:function(l){return'undefined'==typeof l?this.options.labelWidth:(this.options.labelWidth=l,this._width(),this._containerPosition(),this.$element)}},{key:'baseClass',value:function(){return this.options.baseClass}},{key:'wrapperClass',value:function(l){return'undefined'==typeof l?this.options.wrapperClass:(l||(l=g.fn.bootstrapSwitch.defaults.wrapperClass),this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(' ')),this.$wrapper.addClass(this._getClasses(l).join(' ')),this.options.wrapperClass=l,this.$element)}},{key:'radioAllOff',value:function(l){if('undefined'==typeof l)return this.options.radioAllOff;var m=!!l;return this.options.radioAllOff===m?this.$element:(this.options.radioAllOff=m,this.$element)}},{key:'onInit',value:function(l){return'undefined'==typeof l?this.options.onInit:(l||(l=g.fn.bootstrapSwitch.defaults.onInit),this.options.onInit=l,this.$element)}},{key:'onSwitchChange',value:function(l){return'undefined'==typeof l?this.options.onSwitchChange:(l||(l=g.fn.bootstrapSwitch.defaults.onSwitchChange),this.options.onSwitchChange=l,this.$element)}},{key:'destroy',value:function(){var l=this.$element.closest('form');return l.length&&l.off('reset.bootstrapSwitch').removeData('bootstrap-switch'),this.$container.children().not(this.$element).remove(),this.$element.unwrap().unwrap().off('.bootstrapSwitch').removeData('bootstrap-switch'),this.$element}},{key:'_getElementOptions',value:function(){return{state:this.$element.is(':checked'),size:this.$element.data('size'),animate:this.$element.data('animate'),disabled:this.$element.is(':disabled'),readonly:this.$element.is('[readonly]'),indeterminate:this.$element.data('indeterminate'),inverse:this.$element.data('inverse'),radioAllOff:this.$element.data('radio-all-off'),onColor:this.$element.data('on-color'),offColor:this.$element.data('off-color'),onText:this.$element.data('on-text'),offText:this.$element.data('off-text'),labelText:this.$element.data('label-text'),handleWidth:this.$element.data('handle-width'),labelWidth:this.$element.data('label-width'),baseClass:this.$element.data('base-class'),wrapperClass:this.$element.data('wrapper-class')}}},{key:'_width',value:function(){var l=this,m=this.$on.add(this.$off).add(this.$label).css('width',''),n='auto'===this.options.handleWidth?Math.round(Math.max(this.$on.width(),this.$off.width())):this.options.handleWidth;return m.width(n),this.$label.width(function(o,p){return'auto'===l.options.labelWidth?p<n?n:p:l.options.labelWidth}),this._handleWidth=this.$on.outerWidth(),this._labelWidth=this.$label.outerWidth(),this.$container.width(2*this._handleWidth+this._labelWidth),this.$wrapper.width(this._handleWidth+this._labelWidth)}},{key:'_containerPosition',value:function(){var l=this,m=0<arguments.length&&void 0!==arguments[0]?arguments[0]:this.options.state,n=arguments[1];this.$container.css('margin-left',function(){var o=[0,'-'+l._handleWidth+'px'];return l.options.indeterminate?'-'+l._handleWidth/2+'px':m?l.options.inverse?o[1]:o[0]:l.options.inverse?o[0]:o[1]})}},{key:'_init',value:function(){var l=this,m=function(){l.setPrevOptions(),l._width(),l._containerPosition(),setTimeout(function(){if(l.options.animate)return l.$wrapper.addClass(l._getClass('animate'))},50)};if(this.$wrapper.is(':visible'))return void m();var n=window.setInterval(function(){if(l.$wrapper.is(':visible'))return m(),window.clearInterval(n)},50)}},{key:'_elementHandlers',value:function(){var l=this;return this.$element.on({'setPreviousOptions.bootstrapSwitch':this.setPrevOptions.bind(this),'previousState.bootstrapSwitch':function(){l.options=l.prevOptions,l.options.indeterminate&&l.$wrapper.addClass(l._getClass('indeterminate')),l.$element.prop('checked',l.options.state).trigger('change.bootstrapSwitch',!0)},'change.bootstrapSwitch':function(n,o){n.preventDefault(),n.stopImmediatePropagation();var p=l.$element.is(':checked');l._containerPosition(p),p===l.options.state||(l.options.state=p,l.$wrapper.toggleClass(l._getClass('off')).toggleClass(l._getClass('on')),!o&&(l.$element.is(':radio')&&g('[name="'+l.$element.attr('name')+'"]').not(l.$element).prop('checked',!1).trigger('change.bootstrapSwitch',!0),l.$element.trigger('switchChange.bootstrapSwitch',[p])))},'focus.bootstrapSwitch':function(n){n.preventDefault(),l.$wrapper.addClass(l._getClass('focused'))},'blur.bootstrapSwitch':function(n){n.preventDefault(),l.$wrapper.removeClass(l._getClass('focused'))},'keydown.bootstrapSwitch':function(n){!n.which||l.options.disabled||l.options.readonly||(37===n.which||39===n.which)&&(n.preventDefault(),n.stopImmediatePropagation(),l.state(39===n.which))}})}},{key:'_handleHandlers',value:function(){var l=this;return this.$on.on('click.bootstrapSwitch',function(m){return m.preventDefault(),m.stopPropagation(),l.state(!1),l.$element.trigger('focus.bootstrapSwitch')}),this.$off.on('click.bootstrapSwitch',function(m){return m.preventDefault(),m.stopPropagation(),l.state(!0),l.$element.trigger('focus.bootstrapSwitch')})}},{key:'_labelHandlers',value:function(){var l=this;this.$label.on({click:function(o){o.stopPropagation()},'mousedown.bootstrapSwitch touchstart.bootstrapSwitch':function(o){l._dragStart||l.options.disabled||l.options.readonly||(o.preventDefault(),o.stopPropagation(),l._dragStart=(o.pageX||o.originalEvent.touches[0].pageX)-parseInt(l.$container.css('margin-left'),10),l.options.animate&&l.$wrapper.removeClass(l._getClass('animate')),l.$element.trigger('focus.bootstrapSwitch'))},'mousemove.bootstrapSwitch touchmove.bootstrapSwitch':function(o){if(null!=l._dragStart){var p=(o.pageX||o.originalEvent.touches[0].pageX)-l._dragStart;o.preventDefault(),p<-l._handleWidth||0<p||(l._dragEnd=p,l.$container.css('margin-left',l._dragEnd+'px'))}},'mouseup.bootstrapSwitch touchend.bootstrapSwitch':function(o){if(l._dragStart){if(o.preventDefault(),l.options.animate&&l.$wrapper.addClass(l._getClass('animate')),l._dragEnd){var p=l._dragEnd>-(l._handleWidth/2);l._dragEnd=!1,l.state(l.options.inverse?!p:p)}else l.state(!l.options.state);l._dragStart=!1}},'mouseleave.bootstrapSwitch':function(){l.$label.trigger('mouseup.bootstrapSwitch')}})}},{key:'_externalLabelHandler',value:function(){var l=this,m=this.$element.closest('label');m.on('click',function(n){n.preventDefault(),n.stopImmediatePropagation(),n.target===m[0]&&l.toggleState()})}},{key:'_formHandler',value:function(){var l=this.$element.closest('form');l.data('bootstrap-switch')||l.on('reset.bootstrapSwitch',function(){window.setTimeout(function(){l.find('input').filter(function(){return g(this).data('bootstrap-switch')}).each(function(){return g(this).bootstrapSwitch('state',this.checked)})},1)}).data('bootstrap-switch',!0)}},{key:'_getClass',value:function(l){return this.options.baseClass+'-'+l}},{key:'_getClasses',value:function(l){return g.isArray(l)?l.map(this._getClass.bind(this)):[this._getClass(l)]}}]),j}();g.fn.bootstrapSwitch=function(j){for(var l=arguments.length,m=Array(1<l?l-1:0),n=1;n<l;n++)m[n-1]=arguments[n];return Array.prototype.reduce.call(this,function(o,p){var q=g(p),r=q.data('bootstrap-switch'),s=r||new h(p,j);return r||q.data('bootstrap-switch',s),'string'==typeof j?s[j].apply(s,m):o},this)},g.fn.bootstrapSwitch.Constructor=h,g.fn.bootstrapSwitch.defaults={state:!0,size:null,animate:!0,disabled:!1,readonly:!1,indeterminate:!1,inverse:!1,radioAllOff:!1,onColor:'primary',offColor:'default',onText:'ON',offText:'OFF',labelText:'&nbsp',handleWidth:'auto',labelWidth:'auto',baseClass:'bootstrap-switch',wrapperClass:'wrapper',onInit:function(){},onSwitchChange:function(){}}});
516
+
517
+ /* caret v1.3.3 | https://github.com/accursoft/caret | Copyright (c) 2009 Gideon Sireling. Released under the BSD3 license. */
518
+ !function(e){e.fn.caret=function(e){var t=this[0],n="true"===t.contentEditable;if(0==arguments.length){if(window.getSelection){if(n){t.focus();var o=window.getSelection().getRangeAt(0),r=o.cloneRange();return r.selectNodeContents(t),r.setEnd(o.endContainer,o.endOffset),r.toString().length}return t.selectionStart}if(document.selection){if(t.focus(),n){var o=document.selection.createRange(),r=document.body.createTextRange();return r.moveToElementText(t),r.setEndPoint("EndToEnd",o),r.text.length}var e=0,c=t.createTextRange(),r=document.selection.createRange().duplicate(),a=r.getBookmark();for(c.moveToBookmark(a);0!==c.moveStart("character",-1);)e++;return e}return t.selectionStart?t.selectionStart:0}if(-1==e&&(e=this[n?"text":"val"]().length),window.getSelection)n?(t.focus(),window.getSelection().collapse(t.firstChild,e)):t.setSelectionRange(e,e);else if(document.body.createTextRange)if(n){var c=document.body.createTextRange();c.moveToElementText(t),c.moveStart("character",e),c.collapse(!0),c.select()}else{var c=t.createTextRange();c.move("character",e),c.select()}return n||t.focus(),e}}(jQuery);
519
+
520
+ /* jQuery tagEditor v1.0.21 | https://github.com/Pixabay/jQuery-tagEditor | Copyright (c) 2013 Pixabay. Licensed under the MIT license. */
521
  !function(t){t.fn.tagEditorInput=function(){var e=" ",i=t(this),a=parseInt(i.css("fontSize")),r=t("<span/>").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:i.css("fontSize"),fontFamily:i.css("fontFamily"),fontWeight:i.css("fontWeight"),letterSpacing:i.css("letterSpacing"),whiteSpace:"nowrap"}),l=function(){if(e!==(e=i.val())){r.text(e);var t=r.width()+a;20>t&&(t=20),t!=i.width()&&i.width(t)}};return r.insertAfter(i),i.bind("keyup keydown focus",l)},t.fn.tagEditor=function(e,a,r){function l(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}var n,o=t.extend({},t.fn.tagEditor.defaults,e),c=this;if(o.dregex=new RegExp("["+o.delimiter.replace("-","-")+"]","g"),"string"==typeof e){var s=[];return c.each(function(){var i=t(this),l=i.data("options"),n=i.next(".tag-editor");if("getTags"==e)s.push({field:i[0],editor:n,tags:n.data("tags")});else if("addTag"==e){if(l.maxTags&&n.data("tags").length>=l.maxTags)return!1;t('<li><div class="tag-editor-spacer">&nbsp;'+l.delimiter[0]+'</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>').appendTo(n).find(".tag-editor-tag").html('<input type="text" maxlength="'+l.maxLength+'">').addClass("active").find("input").val(a).blur(),r?t(".placeholder",n).remove():n.click()}else"removeTag"==e?(t(".tag-editor-tag",n).filter(function(){return t(this).text()==a}).closest("li").find(".tag-editor-delete").click(),r||n.click()):"destroy"==e&&i.removeClass("tag-editor-hidden-src").removeData("options").off("focus.tag-editor").next(".tag-editor").remove()}),"getTags"==e?s:this}return window.getSelection&&t(document).off("keydown.tag-editor").on("keydown.tag-editor",function(e){if(8==e.which||46==e.which||e.ctrlKey&&88==e.which){try{var a=getSelection(),r="INPUT"!=document.activeElement.tagName?t(a.getRangeAt(0).startContainer.parentNode).closest(".tag-editor"):0}catch(e){r=0}if(a.rangeCount>0&&r&&r.length){var l=[],n=a.toString().split(r.prev().data("options").dregex);for(i=0;i<n.length;i++){var o=t.trim(n[i]);o&&l.push(o)}return t(".tag-editor-tag",r).each(function(){~t.inArray(t(this).text(),l)&&t(this).closest("li").find(".tag-editor-delete").click()}),!1}}}),c.each(function(){function e(){!o.placeholder||c.length||t(".deleted, .placeholder, input",s).length||s.append('<li class="placeholder"><div>'+o.placeholder+"</div></li>")}function i(i){var a=c.toString();c=t(".tag-editor-tag:not(.deleted)",s).map(function(e,i){var a=t.trim(t(this).hasClass("active")?t(this).find("input").val():t(i).text());return a?a:void 0}).get(),s.data("tags",c),r.val(c.join(o.delimiter[0])),i||a!=c.toString()&&o.onChange(r,s,c),e()}function a(e){for(var a,n=e.closest("li"),d=e.val().replace(/ +/," ").split(o.dregex),g=e.data("old_tag"),f=c.slice(0),h=!1,u=0;u<d.length;u++)if(v=t.trim(d[u]).slice(0,o.maxLength),o.forceLowercase&&(v=v.toLowerCase()),a=o.beforeTagSave(r,s,f,g,v),v=a||v,a!==!1&&v&&(o.removeDuplicates&&~t.inArray(v,f)&&t(".tag-editor-tag",s).each(function(){t(this).text()==v&&t(this).closest("li").remove()}),f.push(v),n.before('<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag">'+l(v)+'</div><div class="tag-editor-delete"><i></i></div></li>'),o.maxTags&&f.length>=o.maxTags)){h=!0;break}e.attr("maxlength",o.maxLength).removeData("old_tag").val(""),h?e.blur():e.focus(),i()}var r=t(this),c=[],s=t("<ul "+(o.clickDelete?'oncontextmenu="return false;" ':"")+'class="tag-editor"></ul>').insertAfter(r);r.addClass("tag-editor-hidden-src").data("options",o).on("focus.tag-editor",function(){s.click()}),s.append('<li style="width:1px">&nbsp;</li>');var d='<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>';s.click(function(e,i){var a,r,l=99999;if(!window.getSelection||""==getSelection())return o.maxTags&&s.data("tags").length>=o.maxTags?(s.find("input").blur(),!1):(n=!0,t("input:focus",s).blur(),n?(n=!0,t(".placeholder",s).remove(),i&&i.length?r="before":t(".tag-editor-tag",s).each(function(){var n=t(this),o=n.offset(),c=o.left,s=o.top;e.pageY>=s&&e.pageY<=s+n.height()&&(e.pageX<c?(r="before",a=c-e.pageX):(r="after",a=e.pageX-c-n.width()),l>a&&(l=a,i=n))}),"before"==r?t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click():"after"==r?t(d).insertAfter(i.closest("li")).find(".tag-editor-tag").click():t(d).appendTo(s).find(".tag-editor-tag").click(),!1):!1)}),s.on("click",".tag-editor-delete",function(){if(t(this).prev().hasClass("active"))return t(this).closest("li").find("input").caret(-1),!1;var a=t(this).closest("li"),l=a.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,l.text())===!1?!1:(l.addClass("deleted").animate({width:0},o.animateDelete,function(){a.remove(),e()}),i(),!1)}),o.clickDelete&&s.on("mousedown",".tag-editor-tag",function(a){if(a.ctrlKey||a.which>1){var l=t(this).closest("li"),n=l.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,n.text())===!1?!1:(n.addClass("deleted").animate({width:0},o.animateDelete,function(){l.remove(),e()}),i(),!1)}}),s.on("click",".tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1))return!1;if(!t(this).hasClass("active")){var i=t(this).text(),a=Math.abs((t(this).offset().left-e.pageX)/t(this).width()),r=parseInt(i.length*a),n=t(this).html('<input type="text" maxlength="'+o.maxLength+'" value="'+l(i)+'">').addClass("active").find("input");if(n.data("old_tag",i).tagEditorInput().focus().caret(r),o.autocomplete){var c=t.extend({},o.autocomplete),d="select"in c?o.autocomplete.select:"";c.select=function(e,i){d&&d(e,i),setTimeout(function(){s.trigger("click",[t(".active",s).find("input").closest("li").next("li").find(".tag-editor-tag")])},20)},n.autocomplete(c)}}return!1}),s.on("blur","input",function(d){d.stopPropagation();var g=t(this),f=g.data("old_tag"),h=t.trim(g.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(h){if(h.indexOf(o.delimiter[0])>=0)return void a(g);if(h!=f)if(o.forceLowercase&&(h=h.toLowerCase()),cb_val=o.beforeTagSave(r,s,c,f,h),h=cb_val||h,cb_val===!1){if(f)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}else o.removeDuplicates&&t(".tag-editor-tag:not(.active)",s).each(function(){t(this).text()==h&&t(this).closest("li").remove()})}else{if(f&&o.beforeTagDelete(r,s,c,f)===!1)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}g.parent().html(l(h)).removeClass("active"),h!=f&&i(),e()});var g;s.on("paste","input",function(){t(this).removeAttr("maxlength"),g=t(this),setTimeout(function(){a(g)},30)});var f;s.on("keypress","input",function(e){o.delimiter.indexOf(String.fromCharCode(e.which))>=0&&(f=t(this),setTimeout(function(){a(f)},20))}),s.on("keydown","input",function(e){var i=t(this);if((37==e.which||!o.autocomplete&&38==e.which)&&!i.caret()||8==e.which&&!i.val()){var a=i.closest("li").prev("li").find(".tag-editor-tag");return a.length?a.click().find("input").caret(-1):!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags||t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click(),!1}if((39==e.which||!o.autocomplete&&40==e.which)&&i.caret()==i.val().length){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(9==e.which){if(e.shiftKey){var a=i.closest("li").prev("li").find(".tag-editor-tag");if(a.length)a.click().find("input").caret(0);else{if(!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags)return r.attr("disabled","disabled"),void setTimeout(function(){r.removeAttr("disabled")},30);t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click()}return!1}var l=i.closest("li").next("li").find(".tag-editor-tag");if(l.length)l.click().find("input").caret(0);else{if(!i.val())return;s.click()}return!1}if(!(46!=e.which||t.trim(i.val())&&i.caret()!=i.val().length)){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(13==e.which)return s.trigger("click",[i.closest("li").next("li").find(".tag-editor-tag")]),o.maxTags&&s.data("tags").length>=o.maxTags&&s.find("input").blur(),!1;if(36!=e.which||i.caret()){if(35==e.which&&i.caret()==i.val().length)s.find(".tag-editor-tag").last().click();else if(27==e.which)return i.val(i.data("old_tag")?i.data("old_tag"):"").blur(),!1}else s.find(".tag-editor-tag").first().click()});for(var h=o.initialTags.length?o.initialTags:r.val().split(o.dregex),u=0;u<h.length&&!(o.maxTags&&u>=o.maxTags);u++){var v=t.trim(h[u].replace(/ +/," "));v&&(o.forceLowercase&&(v=v.toLowerCase()),c.push(v),s.append('<li><div class="tag-editor-spacer">&nbsp;'+o.delimiter[0]+'</div><div class="tag-editor-tag">'+l(v)+'</div><div class="tag-editor-delete"><i></i></div></li>'))}i(!0),o.sortable&&t.fn.sortable&&s.sortable({distance:5,cancel:".tag-editor-spacer, input",helper:"clone",update:function(){i()}})})},t.fn.tagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,delimiter:",;",placeholder:"",forceLowercase:!0,removeDuplicates:!0,clickDelete:!1,animateDelete:175,sortable:!0,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}}(jQuery);
admin/view/index.php CHANGED
@@ -1,169 +1,164 @@
1
- <?php if ( !function_exists( 'add_action' ) ) exit(); ?>
2
-
3
- <div class="wrap slimstat">
4
- <h2><?php echo wp_slimstat_admin::$screens_info[ $_GET[ 'page' ] ][ 'title' ] ?></h2>
5
-
6
- <div class="notice slimstat-notice slimstat-tooltip-content" style="background-color:#ffa;border:0;padding:10px"><?php _e( '<strong>AdBlock browser extension detected</strong> - If you see this notice, it means that your browser is not loading our stylesheet and/or Javascript files correctly. This could be caused by an overzealous ad blocker feature enabled in your browser (AdBlock Plus and friends). <a href="https://slimstat.freshdesk.com/support/solutions/articles/12000000414-the-reports-are-not-being-rendered-correctly-or-buttons-do-not-work" target="_blank">Please make sure to add an exception</a> to your configuration and allow the browser to load these assets.', 'wp-slimstat' ); ?></div>
7
-
8
- <form action="<?php echo wp_slimstat_reports::fs_url(); ?>" method="post" id="slimstat-filters-form">
9
- <fieldset id="slimstat-filters"><?php
10
- $filter_name_html = '<label for="slimstat-filter-name">Filter by</label><select name="f" id="slimstat-filter-name"><option value="" disabled selected>' . __( 'Filter', 'wp-slimstat' ) . '</option>';
11
- foreach ( wp_slimstat_db::$columns_names as $a_filter_label => $a_filter_info ) {
12
- $filter_name_html .= "<option value='$a_filter_label'>{$a_filter_info[0]}</option>";
13
- }
14
- $filter_name_html .= '</select>';
15
-
16
- $filter_operator_html = '<label for="slimstat-filter-operator">Filter operator</label><select name="o" id="slimstat-filter-operator">';
17
- foreach ( wp_slimstat_db::$operator_names as $a_operator_label => $a_operator_name ) {
18
- $filter_operator_html .= "<option value='$a_operator_label'>$a_operator_name</option>";
19
- }
20
- $filter_operator_html .= '</select>';
21
-
22
- $filter_value_html = '<label for="slimstat-filter-value">Filter value</label><input type="text" class="text" name="v" id="slimstat-filter-value" value="" size="20">';
23
-
24
- if ( wp_slimstat::$settings[ 'enable_sov' ] == 'on' ) {
25
- echo $filter_value_html.$filter_operator_html . $filter_name_html;
26
- }
27
- else {
28
- echo $filter_name_html.$filter_operator_html . $filter_value_html;
29
- }
30
-
31
- echo '<input type="submit" value="' . __( 'Apply', 'wp-slimstat' ) . '" class="button-secondary">';
32
-
33
- $saved_filters = get_option( 'slimstat_filters', array() );
34
- if ( !empty( $saved_filters ) ) {
35
- echo '<a href="#" id="slimstat-load-saved-filters" class="button-secondary noslimstat" title="Saved Filters">' . __( 'Load', 'wp-slimstat' ) . '</a>';
36
- }
37
- ?></fieldset><!-- #slimstat-filters -->
38
-
39
- <fieldset id="slimstat-date-filters" class="wp-ui-highlight">
40
- <a href="#" class="noslimstat"><?php
41
- if ( !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'hour' ] ) || !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval_hours' ] ) ) {
42
- echo gmdate( wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ], wp_slimstat_db::$filters_normalized['utime']['start']).' - ';
43
-
44
- $end_format = ( date( 'Ymd', wp_slimstat_db::$filters_normalized[ 'utime' ][ 'start' ] ) != date( 'Ymd', wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] ) ) ? wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ] : wp_slimstat::$settings[ 'time_format' ];
45
- echo gmdate( $end_format, wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] );
46
- }
47
- else {
48
- $start_date = gmdate( wp_slimstat::$settings[ 'date_format' ], wp_slimstat_db::$filters_normalized[ 'utime' ][ 'start' ] );
49
- $end_date = gmdate( wp_slimstat::$settings[ 'date_format' ], wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] );
50
-
51
- if ( $start_date == $end_date ) {
52
- echo ucwords( $start_date );
53
- }
54
- else {
55
- echo ucwords( $start_date ) . ' &ndash; ' . ucwords( $end_date );
56
- }
57
- }
58
- ?></a>
59
- <div class="dropdown">
60
- <div id="slimstat-quick-filters">
61
- <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals today&&&interval equals -1' ) ?>"><?php _e( 'Today', 'wp-slimstat' ) ?></a>
62
- <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals yesterday&&&interval equals -1' ) ?>"><?php _e( 'Yesterday', 'wp-slimstat' ) ?></a>
63
- <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals today&&&interval equals -7' ) ?>"><?php _e( 'Last 7 Days', 'wp-slimstat' ) ?></a>
64
- <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals today&&&interval equals -30' ) ?>"><?php _e( 'Last 30 Days', 'wp-slimstat' ) ?></a>
65
- <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals today&&&interval equals -90' ) ?>"><?php _e( 'Last 90 Days', 'wp-slimstat' ) ?></a>
66
- <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals today&&&interval equals -365' ) ?>"><?php _e( 'Last 365 Days', 'wp-slimstat' ) ?></a>
67
- </div>
68
-
69
- <strong><?php _e( 'Date Range', 'wp-slimstat' ) ?></strong>
70
- <label for="slimstat-filter-day">Day</label>
71
- <input type="text" name="day" id="slimstat-filter-day" placeholder="<?php _e( 'Day', 'wp-slimstat' ) ?>" value="">
72
-
73
- <label for="slimstat-filter-month">Month</label>
74
- <select name="month" id="slimstat-filter-month">
75
- <option value="0"><?php _e( 'Month', 'wp-slimstat' ) ?></option><?php
76
- for ( $i = 1; $i <= 12; $i++ ) {
77
- echo "<option value='$i'>" . $GLOBALS[ 'wp_locale' ]->get_month( $i ) . "</option>";
78
- }
79
- ?>
80
- </select>
81
-
82
- <label for="slimstat-filter-year">Year</label>
83
- <input type="text" name="year" id="slimstat-filter-year" placeholder="<?php _e('Year','wp-slimstat') ?>" value="">
84
- @
85
- <label for="slimstat-filter-hour">Hour</label>
86
- <input type="text" name="hour" id="slimstat-filter-hour" placeholder="<?php _e('Hour','wp-slimstat') ?>" class="short" value="">:
87
-
88
- <label for="slimstat-filter-minute">Minute</label>
89
- <input type="text" name="minute" id="slimstat-filter-minute" placeholder="<?php _e('Min','wp-slimstat') ?>" class="short" value="">
90
- <input type="hidden" class="slimstat-filter-date" name="slimstat-filter-date" value=""/>
91
- <br/>
92
-
93
- <label for="slimstat-filter-interval_direction">Direction</label>
94
- <select name="interval_direction" class="short" id="slimstat-filter-interval_direction">
95
- <option value="1" <?php selected( wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval_direction' ], 1 ) ?>>-</option>
96
- <option value="2" <?php selected( wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval_direction' ], 2 ) ?>>+</option>
97
- </select>
98
-
99
- <label for="slimstat-filter-interval">Days in interval</label>
100
- <input type="text" name="interval" id="slimstat-filter-interval" placeholder="<?php _e('days', 'wp-slimstat') ?>" class="short" value="">,
101
-
102
- <label for="slimstat-filter-interval_hours">Hours in interval</label>
103
- <input type="text" name="interval_hours" id="slimstat-filter-interval_hours" placeholder="<?php _e('hours', 'wp-slimstat') ?>" class="short" value="">:
104
-
105
- <label for="slimstat-filter-interval_minutes">Minutes in interval</label>
106
- <input type="text" name="interval_minutes" id="slimstat-filter-interval_minutes" placeholder="<?php _e('mins', 'wp-slimstat') ?>" class="short" value="">
107
-
108
- <input type="submit" value="<?php _e( 'Apply', 'wp-slimstat' ) ?>" class="button button-primary noslimstat">
109
-
110
- <?php
111
- if ( !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'day' ] ) || !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'month' ] ) || !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'year' ] ) || !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval' ] ) || !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval_hours' ] ) || !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval_minutes' ] ) ) {
112
- echo '<a class="slimstat-filter-link button button-secondary noslimstat" data-reset-filters="true" href="' . wp_slimstat_reports::fs_url() . '">' . __( 'Reset Filters', 'wp-slimstat' ) . '</a>';
113
- }
114
- ?>
115
- </div>
116
- </fieldset><!-- .slimstat-date-filters -->
117
-
118
- <?php foreach( wp_slimstat_db::$filters_normalized[ 'columns' ] as $a_key => $a_details ): ?>
119
- <input type="hidden" name="fs[<?php echo $a_key ?>]" class="slimstat-post-filter" value="<?php echo htmlspecialchars($a_details[0].' '.$a_details[1]) ?>"/>
120
- <?php endforeach ?>
121
-
122
- <?php foreach ( wp_slimstat_db::$filters_normalized[ 'date' ] as $a_key => $a_value ) : if ( !empty( $a_value ) ): ?>
123
- <input type="hidden" name="fs[<?php echo $a_key ?>]" class="slimstat-post-filter" value="equals <?php echo htmlspecialchars( $a_value ) ?>"/>
124
- <?php endif; endforeach; ?>
125
-
126
- <?php foreach( wp_slimstat_db::$filters_normalized[ 'misc' ] as $a_key => $a_value ): if ( !empty( $a_value ) ): ?>
127
- <input type="hidden" name="fs[<?php echo $a_key ?>]" class="slimstat-post-filter" value="equals <?php echo htmlspecialchars( $a_value ) ?>"/>
128
- <?php endif; endforeach; ?>
129
- </form>
130
- <?php
131
- if ( !file_exists( wp_slimstat::$maxmind_path ) && ( empty( wp_slimstat::$settings[ 'no_maxmind_warning' ] ) || wp_slimstat::$settings[ 'no_maxmind_warning' ] != 'on' ) ) {
132
- wp_slimstat_admin::show_alert_message( sprintf( __( "<a href='%s' class='noslimstat'>Install MaxMind's GeoLite DB</a> to determine your visitors' country of origin.", 'wp-slimstat' ), self::$config_url . '6#wp-slimstat-external-data-files' ) . '<a id="slimstat-hide-geolite-notice" class="slimstat-font-cancel slimstat-float-right noslimstat" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
133
- }
134
-
135
- if ( !file_exists( slim_browser::$browscap_autoload_path ) && ( empty( wp_slimstat::$settings[ 'no_browscap_warning' ] ) || wp_slimstat::$settings[ 'no_browscap_warning' ] != 'on' ) ) {
136
- wp_slimstat_admin::show_alert_message( sprintf( __( "Install the Browscap <a href='%s' class='noslimstat'>User Agent Database</a> to accurately determine your visitors' browser and operating system.", 'wp-slimstat' ), self::$config_url . '6#wp-slimstat-external-data-files' ) . '<a id="slimstat-hide-browscap-notice" class="slimstat-font-cancel slimstat-float-right noslimstat" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
137
- }
138
-
139
- // Path to wp-content folder, used to detect caching plugins via advanced-cache.php
140
- if ( file_exists( dirname( dirname( dirname( dirname( plugin_dir_path( __FILE__ ) ) ) ) ) . '/advanced-cache.php' ) && ( empty( wp_slimstat::$settings[ 'no_caching_warning' ] ) || wp_slimstat::$settings[ 'no_caching_warning' ] != 'on' ) && ( empty( wp_slimstat::$settings[ 'javascript_mode' ] ) || wp_slimstat::$settings[ 'javascript_mode' ] != 'on' ) ) {
141
- wp_slimstat_admin::show_alert_message( sprintf( __( "A caching plugin might be enabled on your website. Please <a href='%s' target='_blank' class='noslimstat'>make sure to configure</a> Slimstat Analytics accordingly, to get accurate information.", 'wp-slimstat' ), 'https://slimstat.freshdesk.com/support/solutions/articles/5000528524-i-am-using-w3-total-cache-or-wp-super-cache-hypercache-etc-and-it-looks-like-slimstat-is-not-tra' ) . '<a id="slimstat-hide-caching-notice" class="slimstat-font-cancel slimstat-float-right" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
142
- }
143
-
144
- $filters_html = wp_slimstat_reports::get_filters_html( wp_slimstat_db::$filters_normalized[ 'columns' ] );
145
- if ( !empty( $filters_html ) ) {
146
- echo "<div id='slimstat-current-filters'>$filters_html</div>";
147
- }
148
- ?>
149
-
150
- <div class="meta-box-sortables">
151
- <form method="get" action=""><input type="hidden" id="meta-box-order-nonce" name="meta-box-order-nonce" value="<?php echo wp_create_nonce('meta-box-order') ?>" /></form><?php
152
-
153
- foreach( wp_slimstat_reports::$reports_info as $a_report_id => $a_report_info ) {
154
- if ( !is_array( $a_report_info[ 'classes' ] ) ) {
155
- continue;
156
- }
157
-
158
- wp_slimstat_reports::report_header( $a_report_id );
159
-
160
- // Third party reports can add their own methods via the callback parameter
161
- if ( !in_array( 'hidden', $a_report_info[ 'classes' ] ) ) {
162
- wp_slimstat_reports::callback_wrapper( array( 'id' => $a_report_id ) );
163
- }
164
-
165
- wp_slimstat_reports::report_footer();
166
- }
167
- ?></div>
168
- </div>
169
  <div id="slimstat-modal-dialog"></div>
1
+ <?php if ( !function_exists( 'add_action' ) ) exit(); ?>
2
+
3
+ <div class="wrap slimstat">
4
+ <h2><?php echo wp_slimstat_admin::$screens_info[ $_GET[ 'page' ] ][ 'title' ] ?></h2>
5
+
6
+ <div class="notice slimstat-notice slimstat-tooltip-content" style="background-color:#ffa;border:0;padding:10px"><?php _e( '<strong>AdBlock browser extension detected</strong> - If you see this notice, it means that your browser is not loading our stylesheet and/or Javascript files correctly. This could be caused by an overzealous ad blocker feature enabled in your browser (AdBlock Plus and friends). <a href="https://slimstat.freshdesk.com/support/solutions/articles/12000000414-the-reports-are-not-being-rendered-correctly-or-buttons-do-not-work" target="_blank">Please make sure to add an exception</a> to your configuration and allow the browser to load these assets.', 'wp-slimstat' ); ?></div>
7
+
8
+ <form action="<?php echo wp_slimstat_reports::fs_url(); ?>" method="post" id="slimstat-filters-form">
9
+ <fieldset id="slimstat-filters"><?php
10
+ $filter_name_html = '<label for="slimstat-filter-name">Filter by</label><select name="f" id="slimstat-filter-name"><option value="" disabled selected>' . __( 'Filter', 'wp-slimstat' ) . '</option>';
11
+ foreach ( wp_slimstat_db::$columns_names as $a_filter_label => $a_filter_info ) {
12
+ $filter_name_html .= "<option value='$a_filter_label'>{$a_filter_info[0]}</option>";
13
+ }
14
+ $filter_name_html .= '</select>';
15
+
16
+ $filter_operator_html = '<label for="slimstat-filter-operator">Filter operator</label><select name="o" id="slimstat-filter-operator">';
17
+ foreach ( wp_slimstat_db::$operator_names as $a_operator_label => $a_operator_name ) {
18
+ $filter_operator_html .= "<option value='$a_operator_label'>$a_operator_name</option>";
19
+ }
20
+ $filter_operator_html .= '</select>';
21
+
22
+ $filter_value_html = '<label for="slimstat-filter-value">Filter value</label><input type="text" class="text" name="v" id="slimstat-filter-value" value="" size="20">';
23
+
24
+ if ( wp_slimstat::$settings[ 'enable_sov' ] == 'on' ) {
25
+ echo $filter_value_html.$filter_operator_html . $filter_name_html;
26
+ }
27
+ else {
28
+ echo $filter_name_html.$filter_operator_html . $filter_value_html;
29
+ }
30
+
31
+ echo '<input type="submit" value="' . __( 'Apply', 'wp-slimstat' ) . '" class="button-secondary">';
32
+
33
+ $saved_filters = get_option( 'slimstat_filters', array() );
34
+ if ( !empty( $saved_filters ) ) {
35
+ echo '<a href="#" id="slimstat-load-saved-filters" class="button-secondary noslimstat" title="Saved Filters">' . __( 'Load', 'wp-slimstat' ) . '</a>';
36
+ }
37
+ ?></fieldset><!-- #slimstat-filters -->
38
+
39
+ <fieldset id="slimstat-date-filters" class="wp-ui-highlight">
40
+ <a href="#" class="noslimstat"><?php
41
+ if ( !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'hour' ] ) || !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval_hours' ] ) ) {
42
+ echo gmdate( wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ], wp_slimstat_db::$filters_normalized['utime']['start']).' - ';
43
+
44
+ $end_format = ( date( 'Ymd', wp_slimstat_db::$filters_normalized[ 'utime' ][ 'start' ] ) != date( 'Ymd', wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] ) ) ? wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ] : wp_slimstat::$settings[ 'time_format' ];
45
+ echo gmdate( $end_format, wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] );
46
+ }
47
+ else {
48
+ $start_date = gmdate( wp_slimstat::$settings[ 'date_format' ], wp_slimstat_db::$filters_normalized[ 'utime' ][ 'start' ] );
49
+ $end_date = gmdate( wp_slimstat::$settings[ 'date_format' ], wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] );
50
+
51
+ if ( $start_date == $end_date ) {
52
+ echo ucwords( $start_date );
53
+ }
54
+ else {
55
+ echo ucwords( $start_date ) . ' &ndash; ' . ucwords( $end_date );
56
+ }
57
+ }
58
+ ?></a>
59
+ <div class="dropdown">
60
+ <div id="slimstat-quick-filters">
61
+ <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals today&&&interval equals -1' ) ?>"><?php _e( 'Today', 'wp-slimstat' ) ?></a>
62
+ <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals yesterday&&&interval equals -1' ) ?>"><?php _e( 'Yesterday', 'wp-slimstat' ) ?></a>
63
+ <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals today&&&interval equals -7' ) ?>"><?php _e( 'Last 7 Days', 'wp-slimstat' ) ?></a>
64
+ <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals today&&&interval equals -30' ) ?>"><?php _e( 'Last 30 Days', 'wp-slimstat' ) ?></a>
65
+ <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals today&&&interval equals -90' ) ?>"><?php _e( 'Last 90 Days', 'wp-slimstat' ) ?></a>
66
+ <a class="slimstat-filter-link noslimstat" href="<?php echo wp_slimstat_reports::fs_url( 'strtotime equals today&&&interval equals -365' ) ?>"><?php _e( 'Last 365 Days', 'wp-slimstat' ) ?></a>
67
+ </div>
68
+
69
+ <strong><?php _e( 'Date Range', 'wp-slimstat' ) ?></strong>
70
+
71
+ <label for="slimstat-filter-hour">Hour</label>
72
+ <input type="text" name="hour" id="slimstat-filter-hour" placeholder="<?php _e('Hour','wp-slimstat') ?>" class="short" value="">
73
+
74
+ <label for="slimstat-filter-day">Day</label>
75
+ <input type="text" name="day" id="slimstat-filter-day" placeholder="<?php _e( 'Day', 'wp-slimstat' ) ?>" class="short" value="">
76
+
77
+ <label for="slimstat-filter-month">Month</label>
78
+ <select name="month" id="slimstat-filter-month">
79
+ <option value=""><?php _e( 'Month', 'wp-slimstat' ) ?></option><?php
80
+ for ( $i = 1; $i <= 12; $i++ ) {
81
+ echo "<option value='$i'>" . $GLOBALS[ 'wp_locale' ]->get_month( $i ) . "</option>";
82
+ }
83
+ ?>
84
+ </select>
85
+
86
+ <label for="slimstat-filter-year">Year</label>
87
+ <input type="text" name="year" id="slimstat-filter-year" placeholder="<?php _e('Year','wp-slimstat') ?>" class="short" value="">
88
+
89
+ <input type="hidden" class="slimstat-filter-date" name="slimstat-filter-date" value=""/>
90
+ <br/>
91
+
92
+ <label for="slimstat-filter-interval">Days in interval</label>
93
+ <input type="text" name="interval" id="slimstat-filter-interval" placeholder="<?php _e('&plusmn; days', 'wp-slimstat') ?>" class="short" value="" title="<?php _e( 'To define an interval, enter the number of days (negative to go back in time).', 'wp-slimstat' ) ?>">
94
+
95
+ <label for="slimstat-filter-interval_hours">Hours in interval</label>
96
+ <input type="text" name="interval_hours" id="slimstat-filter-interval_hours" placeholder="<?php _e('&plusmn; hours', 'wp-slimstat') ?>" class="short" value="">
97
+
98
+ <input type="submit" value="<?php _e( 'Apply', 'wp-slimstat' ) ?>" class="button button-primary noslimstat right">
99
+
100
+ <?php
101
+ wp_slimstat::toggle_date_i18n_filters( false );
102
+
103
+ if ( wp_slimstat_db::$filters_normalized[ 'date' ][ 'day' ] != intval( date_i18n( 'j' ) ) ||
104
+ wp_slimstat_db::$filters_normalized[ 'date' ][ 'month' ] != intval( date_i18n( 'n' ) ) ||
105
+ wp_slimstat_db::$filters_normalized[ 'date' ][ 'year' ] != intval( date_i18n( 'Y' ) ) ||
106
+ ( wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval' ] != - abs( wp_slimstat::$settings[ 'posts_column_day_interval' ] ) && wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval' ] != - intval( date_i18n( 'j' ) ) + 1 ) ) {
107
+ echo '<a class="slimstat-filter-link button button-secondary noslimstat" data-reset-filters="true" href="' . wp_slimstat_reports::fs_url() . '">' . __( 'Reset Filters', 'wp-slimstat' ) . '</a>';
108
+ }
109
+ ?>
110
+ </div>
111
+ </fieldset><!-- .slimstat-date-filters -->
112
+
113
+ <?php foreach( wp_slimstat_db::$filters_normalized[ 'columns' ] as $a_key => $a_details ): ?>
114
+ <input type="hidden" name="fs[<?php echo $a_key ?>]" class="slimstat-post-filter" value="<?php echo htmlspecialchars($a_details[0].' '.$a_details[1]) ?>"/>
115
+ <?php endforeach ?>
116
+
117
+ <?php foreach ( wp_slimstat_db::$filters_normalized[ 'date' ] as $a_key => $a_value ) : if ( !empty( $a_value ) ): ?>
118
+ <input type="hidden" name="fs[<?php echo $a_key ?>]" class="slimstat-post-filter" value="equals <?php echo htmlspecialchars( $a_value ) ?>"/>
119
+ <?php endif; endforeach; ?>
120
+
121
+ <?php foreach( wp_slimstat_db::$filters_normalized[ 'misc' ] as $a_key => $a_value ): if ( !empty( $a_value ) ): ?>
122
+ <input type="hidden" name="fs[<?php echo $a_key ?>]" class="slimstat-post-filter" value="equals <?php echo htmlspecialchars( $a_value ) ?>"/>
123
+ <?php endif; endforeach; ?>
124
+ </form>
125
+ <?php
126
+ if ( !file_exists( wp_slimstat::$maxmind_path ) && ( empty( wp_slimstat::$settings[ 'no_maxmind_warning' ] ) || wp_slimstat::$settings[ 'no_maxmind_warning' ] != 'on' ) ) {
127
+ wp_slimstat_admin::show_alert_message( sprintf( __( "<a href='%s' class='noslimstat'>Install MaxMind's GeoLite DB</a> to determine your visitors' country of origin.", 'wp-slimstat' ), self::$config_url . '6#wp-slimstat-external-data-files' ) . '<a id="slimstat-hide-geolite-notice" class="slimstat-font-cancel slimstat-float-right noslimstat" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
128
+ }
129
+
130
+ if ( !file_exists( slim_browser::$browscap_autoload_path ) && ( empty( wp_slimstat::$settings[ 'no_browscap_warning' ] ) || wp_slimstat::$settings[ 'no_browscap_warning' ] != 'on' ) ) {
131
+ wp_slimstat_admin::show_alert_message( sprintf( __( "Install the Browscap <a href='%s' class='noslimstat'>User Agent Database</a> to accurately determine your visitors' browser and operating system.", 'wp-slimstat' ), self::$config_url . '6#wp-slimstat-external-data-files' ) . '<a id="slimstat-hide-browscap-notice" class="slimstat-font-cancel slimstat-float-right noslimstat" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
132
+ }
133
+
134
+ // Path to wp-content folder, used to detect caching plugins via advanced-cache.php
135
+ if ( file_exists( dirname( dirname( dirname( dirname( plugin_dir_path( __FILE__ ) ) ) ) ) . '/advanced-cache.php' ) && ( empty( wp_slimstat::$settings[ 'no_caching_warning' ] ) || wp_slimstat::$settings[ 'no_caching_warning' ] != 'on' ) && ( empty( wp_slimstat::$settings[ 'javascript_mode' ] ) || wp_slimstat::$settings[ 'javascript_mode' ] != 'on' ) ) {
136
+ wp_slimstat_admin::show_alert_message( sprintf( __( "A caching plugin might be enabled on your website. Please <a href='%s' target='_blank' class='noslimstat'>make sure to configure</a> Slimstat Analytics accordingly, to get accurate information.", 'wp-slimstat' ), 'https://slimstat.freshdesk.com/support/solutions/articles/5000528524-i-am-using-w3-total-cache-or-wp-super-cache-hypercache-etc-and-it-looks-like-slimstat-is-not-tra' ) . '<a id="slimstat-hide-caching-notice" class="slimstat-font-cancel slimstat-float-right" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
137
+ }
138
+
139
+ $filters_html = wp_slimstat_reports::get_filters_html( wp_slimstat_db::$filters_normalized[ 'columns' ] );
140
+ if ( !empty( $filters_html ) ) {
141
+ echo "<div id='slimstat-current-filters'>$filters_html</div>";
142
+ }
143
+ ?>
144
+
145
+ <div class="meta-box-sortables">
146
+ <form method="get" action=""><input type="hidden" id="meta-box-order-nonce" name="meta-box-order-nonce" value="<?php echo wp_create_nonce('meta-box-order') ?>" /></form><?php
147
+
148
+ foreach( wp_slimstat_reports::$reports_info as $a_report_id => $a_report_info ) {
149
+ if ( !is_array( $a_report_info[ 'classes' ] ) ) {
150
+ continue;
151
+ }
152
+
153
+ wp_slimstat_reports::report_header( $a_report_id );
154
+
155
+ // Third party reports can add their own methods via the callback parameter
156
+ if ( !in_array( 'hidden', $a_report_info[ 'classes' ] ) ) {
157
+ wp_slimstat_reports::callback_wrapper( array( 'id' => $a_report_id ) );
158
+ }
159
+
160
+ wp_slimstat_reports::report_footer();
161
+ }
162
+ ?></div>
163
+ </div>
 
 
 
 
 
164
  <div id="slimstat-modal-dialog"></div>
admin/view/right-now.php CHANGED
@@ -1,297 +1,297 @@
1
- <?php
2
- // Avoid direct access to this piece of code
3
- if ( !function_exists( 'add_action' ) ) {
4
- exit(0);
5
- }
6
-
7
- if ( wp_slimstat::$settings[ 'async_load' ] == 'on' && ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
8
- return '';
9
- }
10
-
11
- $is_dashboard = empty( $_REQUEST[ 'page' ] ) || $_REQUEST[ 'page' ] != 'slimview1';
12
-
13
- // Available icons
14
- $supported_browser_icons = array('Android','Anonymouse','Baiduspider','BlackBerry','BingBot','CFNetwork','Chrome','Chromium','Default Browser','Edge','Exabot/BiggerBetter','FacebookExternalHit','FeedBurner','Feedfetcher-Google','Firefox','Internet Archive','Googlebot','Google Bot','Google Feedfetcher','Google Web Preview','IE','IEMobile','iPad','iPhone','iPod Touch','Maxthon','Mediapartners-Google','Microsoft-WebDAV','msnbot','Mozilla','NewsGatorOnline','Netscape','Nokia','Opera','Opera Mini','Opera Mobi','Pingdom','Python','PycURL','Safari','W3C_Validator','WordPress','Yahoo! Slurp','YandexBot');
15
- $supported_os_icons = array('android','blackberry os','cellos','chromeos','ios','iphone osx','java','linux','macosx','rim os','symbianos','win7','win8','win8.1','win10','winphone7','winphone7.5','winphone8','winphone8.1','winvista','winxp','unknown');
16
- $supported_browser_types = array(__('Human','wp-slimstat'),__('Bot/Crawler','wp-slimstat'),__('Mobile Device','wp-slimstat'),__('Syndication Reader','wp-slimstat'));
17
-
18
- $plugin_url = plugins_url('', dirname(__FILE__));
19
-
20
- // Get the data
21
- wp_slimstat_db::$debug_message = '';
22
- $all_results = wp_slimstat_db::get_recent( wp_slimstat_reports::$reports_info[ 'slim_p7_02' ][ 'callback_args' ] );
23
- $results = array_slice(
24
- $all_results,
25
- wp_slimstat_db::$filters_normalized[ 'misc' ][ 'start_from' ],
26
- wp_slimstat::$settings[ 'number_results_raw_data' ]
27
- );
28
-
29
- $count_all_results = count( $all_results );
30
- $count_page_results = count( $results );
31
-
32
- // Echo the debug message
33
- echo wp_slimstat_db::$debug_message;
34
-
35
- // Return the results if we are not echoing them (export, email, etc)
36
- if ( isset( $_args[ 'echo' ] ) && $_args[ 'echo' ] === false ) {
37
-
38
- // Massage the data before returning it
39
- if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'on' ) {
40
- for ( $i = 0; $i < $count_page_results; $i++ ) {
41
- $gethostbyaddr = gethostbyaddr( $results[ $i ][ 'ip' ] );
42
- if ( $gethostbyaddr != $host_by_ip && !empty( $gethostbyaddr ) ) {
43
- $results[ $i ][ 'ip' ] .= ', ' . $gethostbyaddr;
44
- }
45
- }
46
- }
47
-
48
- return $results;
49
- }
50
-
51
- if ($count_page_results == 0){
52
- echo '<p class="nodata">'.__('No data to display','wp-slimstat').'</p>';
53
- }
54
- else {
55
-
56
- // Pagination
57
- echo wp_slimstat_reports::report_pagination( $count_page_results, $count_all_results, true, wp_slimstat::$settings[ 'number_results_raw_data' ] );
58
-
59
- // Show delete button? (only those who can access the settings can see it)
60
- $current_user_can_delete = ( current_user_can( wp_slimstat::$settings[ 'capability_can_admin' ] ) && !is_network_admin() );
61
- $delete_row = '';
62
-
63
- // Loop through the results
64
- for ( $i=0; $i < $count_page_results; $i++ ) {
65
- $host_by_ip = $results[ $i ][ 'ip' ];
66
- if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'on' ) {
67
- $gethostbyaddr = gethostbyaddr( $results[ $i ][ 'ip' ] );
68
- if ( $gethostbyaddr != $host_by_ip && !empty( $gethostbyaddr ) ) {
69
- $host_by_ip .= ', ' . $gethostbyaddr;
70
- }
71
- }
72
-
73
- $date_time = "<i class='spaced slimstat-font-clock' title='".__( 'Date and Time', 'wp-slimstat' )."'></i> " . date_i18n( wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ], $results[ $i ][ 'dt' ], true );
74
-
75
- // Print visit header?
76
- if ( $i == 0 || $results[ $i - 1 ][ 'visit_id' ] != $results[ $i ][ 'visit_id' ] || $results[ $i - 1 ][ 'ip' ] != $results[ $i ][ 'ip' ] || $results[ $i - 1 ][ 'browser' ] != $results[ $i ][ 'browser' ] || $results[ $i - 1 ][ 'platform' ] != $results[ $i ][ 'platform' ] || $results[ $i - 1 ][ 'username' ] != $results[ $i ][ 'username' ] ) {
77
-
78
- // Color-coded headers
79
- $highlight_row = !empty($results[$i]['searchterms'])?' is-search-engine':(($results[$i]['browser_type'] != 1)?' is-direct':'');
80
-
81
- // Country
82
- $country_filtered = '';
83
- if ( !empty( $results[ $i ][ 'country' ] ) && $results[ $i ][ 'country' ] != 'xx' ) {
84
- $country_filtered = "<a class='slimstat-filter-link inline-icon' href='" . wp_slimstat_reports::fs_url( 'country equals ' . $results[ $i ][ 'country' ] ) . "'><img class='slimstat-tooltip-trigger' src='$plugin_url/images/flags/{$results[$i]['country']}.png' width='16' height='16' title='" . __( 'c-' . $results[ $i ][ 'country' ], 'wp-slimstat' ) . "'></a>";
85
- }
86
-
87
- // City, if tracked
88
- $city_filtered = '';
89
- if ( !empty( $results[ $i ][ 'city' ] ) ) {
90
- $city_filtered = "<a class='slimstat-filter-link' href='" . wp_slimstat_reports::fs_url( 'city equals ' . $results[ $i ][ 'city' ] ) . "'>{$results[ $i ][ 'city' ]}</a>";
91
- }
92
-
93
- // Browser
94
- if ($results[$i]['browser_version'] == 0) $results[$i]['browser_version'] = '';
95
- $browser_title = ( wp_slimstat::$settings[ 'show_complete_user_agent_tooltip' ] != 'on' ) ? "{$results[ $i ][ 'browser' ]} {$results[ $i ][ 'browser_version' ]}" : $results[ $i ][ 'user_agent' ];
96
- $browser_icon = $plugin_url.'/images/browsers/other-browsers-and-os.png';
97
- if (in_array($results[$i]['browser'], $supported_browser_icons)){
98
- $browser_icon = $plugin_url.'/images/browsers/'.sanitize_title($results[$i]['browser']).'.png';
99
- }
100
- $browser_filtered = "<a class='slimstat-filter-link inline-icon' href='".wp_slimstat_reports::fs_url('browser equals '.$results[$i]['browser'])."'><img class='slimstat-tooltip-trigger' src='$browser_icon' width='16' height='16' title='{$browser_title}'></a>";
101
-
102
- // Platform
103
- $platform_icon = $plugin_url.'/images/browsers/other-browsers-and-os.png';
104
- if (in_array(strtolower($results[$i]['platform']), $supported_os_icons)){
105
- $platform_icon = $plugin_url.'/images/platforms/'.sanitize_title($results[$i]['platform']).'.png';
106
- }
107
- $platform_filtered = "<a class='slimstat-filter-link inline-icon' href='".wp_slimstat_reports::fs_url('platform equals '.$results[$i]['platform'])."'><img class='slimstat-tooltip-trigger' src='$platform_icon' width='16' height='16' title='" . __( $results[ $i ][ 'platform' ], 'wp-slimstat' ) . "'></a>";
108
-
109
- // Browser Type
110
- $browser_type_filtered = '';
111
- if ($results[$i]['browser_type'] != 0){
112
- $browser_type_filtered = "<a class='slimstat-filter-link inline-icon' href='".wp_slimstat_reports::fs_url('browser_type equals '.$results[$i]['browser_type'])."'><img class='slimstat-tooltip-trigger' src='$plugin_url/images/browsers/type{$results[$i]['browser_type']}.png' width='16' height='16' title='{$supported_browser_types[$results[$i]['browser_type']]}'></a>";
113
- }
114
-
115
- // IP Address and user
116
- if (empty($results[$i]['username'])){
117
- $ip_address = "<a class='slimstat-filter-link' href='".wp_slimstat_reports::fs_url('ip equals '.$results[$i]['ip'])."'>$host_by_ip</a>";
118
- }
119
- else{
120
- $display_user_name = $results[ $i ][ 'username' ];
121
- if ( wp_slimstat::$settings[ 'show_display_name' ] == 'on' && strpos( $results[ $i ][ 'notes' ], 'user:' ) !== false ) {
122
- $display_real_name = get_user_by('login', $results[$i]['username']);
123
- if (is_object($display_real_name)) $display_user_name = $display_real_name->display_name;
124
- }
125
- $ip_address = "<a class='slimstat-filter-link' href='".wp_slimstat_reports::fs_url('username equals '.$results[$i]['username'])."'>{$display_user_name}</a>";
126
- $ip_address .= " <a class='slimstat-filter-link' href='".wp_slimstat_reports::fs_url('ip equals '.$results[$i]['ip'])."'>($host_by_ip)</a>";
127
- $highlight_row = (strpos( $results[$i]['notes'], 'user:') !== false)?' is-known-user':' is-known-visitor';
128
-
129
- }
130
-
131
- $whois_pin = '';
132
- if ( is_admin() && !empty( wp_slimstat::$settings[ 'ip_lookup_service' ] ) && !wp_slimstat::is_local_ip_address( $results[ $i ][ 'ip' ] ) ) {
133
- $whois_pin = "<a class='slimstat-font-location-1 whois' href='" . wp_slimstat::$settings[ 'ip_lookup_service' ] . "{$results[ $i ][ 'ip' ]}' target='_blank' title='WHOIS: {$results[ $i ][ 'ip' ]}'></a>";
134
- }
135
-
136
- // Originating IP Address
137
- $other_ip_address = intval( $results[ $i ][ 'other_ip' ] );
138
- if ( !empty( $other_ip_address ) ) {
139
- $other_ip_address = "<a class='slimstat-filter-link' href='" . wp_slimstat_reports::fs_url( 'other_ip equals '. $results[ $i ][ 'other_ip' ] ) . "'>(" . __( 'Originating IP', 'wp-slimstat' ) . ": {$results[$i]['other_ip']})</a>";
140
- }
141
- else {
142
- $other_ip_address = '';
143
- }
144
-
145
- // Plugins
146
- $plugins = '';
147
- if (!empty($results[$i]['plugins'])){
148
- $results[$i]['plugins'] = explode(',', $results[$i]['plugins']);
149
- foreach($results[$i]['plugins'] as $a_plugin){
150
- $a_plugin = trim($a_plugin);
151
- $plugins .= "<a class='slimstat-filter-link inline-icon' href='" . wp_slimstat_reports::fs_url( 'plugins contains ' . $a_plugin ) . "'><img class='slimstat-tooltip-trigger' src='$plugin_url/images/plugins/$a_plugin.png' width='16' height='16' title='" . __( $a_plugin, 'wp-slimstat' ) . "'></a> ";
152
- }
153
- }
154
-
155
- // Screen Resolution
156
- $screen_resolution = '';
157
- if ( !empty( $results[ $i ][ 'screen_width' ] ) && !empty( $results[ $i ][ 'screen_height' ] ) ) {
158
- $screen_resolution = "<span class='pageview-screenres'>{$results[ $i ][ 'screen_width' ]}x{$results[ $i ][ 'screen_height' ]}</span>";
159
- }
160
-
161
- $row_output = "<p class='header$highlight_row'>$browser_filtered $platform_filtered $browser_type_filtered $country_filtered $whois_pin $city_filtered $ip_address $other_ip_address <span class='plugins'>$plugins</span> $screen_resolution</p>";
162
-
163
- // Strip all the filter links, if this information is shown on the frontend
164
- if ( !is_admin() ) {
165
- $row_output = preg_replace('/<a (.*?)>(.*?)<\/a>/', "\\2", $row_output);
166
- }
167
-
168
- echo $row_output;
169
- }
170
-
171
- // Permalink: find post title, if available
172
- $parse_url = parse_url(get_site_url(empty($results[$i]['blog_id'])?1:$results[$i]['blog_id']));
173
- $base_host = $parse_url['host'];
174
- $base_url = '';
175
-
176
- if ( !empty( $results[ $i ][ 'resource' ] ) ) {
177
- if (!empty($results[$i]['blog_id'])){
178
- $base_url = $parse_url['scheme'].'://'.$base_host;
179
- }
180
- $results[$i]['resource'] = "<a class='slimstat-font-logout' target='_blank' title='".htmlentities(__('Open this URL in a new window','wp-slimstat'), ENT_QUOTES, 'UTF-8')."' href='".$base_url.htmlentities($results[$i]['resource'], ENT_QUOTES, 'UTF-8')."'></a> $base_url<a class='slimstat-filter-link' href='".wp_slimstat_reports::fs_url('resource equals ' . htmlentities($results[$i]['resource'], ENT_QUOTES, 'UTF-8') ) . "'>".wp_slimstat_reports::get_resource_title( $results[$i][ 'resource' ], $results[$i][ 'category' ] ).'</a>';
181
- }
182
- else {
183
- if ( !empty( $results[$i][ 'notes' ] ) ) {
184
- $exploded_notes = explode( ';', $results[$i][ 'notes' ] );
185
- foreach ( $exploded_notes as $a_note ) {
186
- if ( strpos( $a_note, 'results:') !== false ) {
187
- $search_terms_info = $results[ $i ][ 'searchterms' ] . ' (' . $a_note . ')';
188
- break;
189
- }
190
- }
191
- }
192
- $results[$i]['resource'] = __('Local search results page','wp-slimstat');
193
- }
194
-
195
- if ( empty( $search_terms_info ) ) {
196
- $search_terms_info = wp_slimstat_reports::get_search_terms_info( $results[ $i ][ 'searchterms' ], $results[ $i ][ 'referer' ] );
197
- }
198
-
199
- // Search Terms, with link to original SERP, and Outbound Resource
200
- if ( !empty( $search_terms_info ) ) {
201
- $results[$i]['searchterms'] = "<i class='spaced slimstat-font-search' title='" . __( 'Search Terms', 'wp-slimstat' ) . "'></i> $search_terms_info";
202
- }
203
- else {
204
- $results[$i]['searchterms'] = '';
205
- }
206
-
207
- // Let's reset this variable for the next item
208
- $search_terms_info = '';
209
-
210
- // Server Latency and Page Speed
211
- $performance = '';
212
- if ( !$is_dashboard && ( !empty( $results[ $i ][ 'server_latency' ] ) || !empty( $results[ $i ][ 'page_performance' ] ) ) ) {
213
- $performance = "<i class='slimstat-font-gauge spaced' title='".__('Server Latency and Page Speed in milliseconds','wp-slimstat')."'></i> ".__('SL','wp-slimstat').": {$results[$i]['server_latency']} / ".__('PS','wp-slimstat').": {$results[$i]['page_performance']}";
214
- }
215
-
216
- // Time on page
217
- $time_on_page = '';
218
- if ( !$is_dashboard && !empty( $results[ $i ][ 'dt_out' ] ) ) {
219
- $duration = $results[ $i ][ 'dt_out' ] - $results[ $i ][ 'dt' ];
220
- $time_on_page = "<i class='slimstat-font-stopwatch spaced' title='" . __( 'Time spent on this page', 'wp-slimstat' ) . "'></i> " . date( ( $duration > 3599 ? 'H:i:s' : 'i:s' ), $duration );
221
- }
222
-
223
- // Pageview Notes
224
- $notes = '';
225
- if ( is_admin() && !empty( $results[ $i ][ 'notes' ] ) ) {
226
- $notes = str_replace(array(';', ':'), array('<br/>', ': '), $results[$i]['notes']);
227
- $notes = "<i class='slimstat-font-edit slimstat-tooltip-trigger'><b class='slimstat-tooltip-content'>{$notes}</b></i>";
228
- }
229
-
230
- // Avoid XSS attacks through the referer URL
231
- $results[ $i ] [ 'referer' ] = str_replace( array( '<', '>', '%22', '%27', '\'', '"', '%3C', '%3E' ), array( '&lt;', '&gt;', '', '', '', '', '&lt;', '&gt;' ), urldecode( $results[ $i ] [ 'referer' ] ) );
232
-
233
- $login_logout = '';
234
- if ( !$is_dashboard ) {
235
- $domain = parse_url( $results[ $i ] [ 'referer' ] );
236
- $domain = !empty( $domain[ 'host' ] ) ? $domain[ 'host' ] : __( 'Invalid Referrer', 'wp-slimstat' );
237
- $results[$i][ 'referer' ] = (!empty($results[$i]['referer']) && empty($results[$i]['searchterms']))?"<a class='spaced slimstat-font-login' target='_blank' title='".htmlentities(__('Open this referrer in a new window','wp-slimstat'), ENT_QUOTES, 'UTF-8')."' href='{$results[$i]['referer']}'></a> $domain":'';
238
- $results[$i][ 'content_type' ] = !empty($results[$i]['content_type'])?"<i class='spaced slimstat-font-doc' title='".__('Content Type','wp-slimstat')."'></i> <a class='slimstat-filter-link' href='".wp_slimstat_reports::fs_url('content_type equals '.$results[$i]['content_type'])."'>{$results[$i]['content_type']}</a> ":'';
239
-
240
- // The Outbound Links field might contain more than one link
241
- if ( !empty( $results[ $i ][ 'outbound_resource' ] ) ) {
242
- $exploded_outbound_resources = explode( ';;;', $results[ $i ][ 'outbound_resource' ] );
243
- $results[$i][ 'outbound_resource' ] = '';
244
- foreach ( $exploded_outbound_resources as $a_resource ) {
245
- $results[ $i ][ 'outbound_resource' ] .= "<a class='inline-icon spaced slimstat-font-logout' target='_blank' title='".htmlentities( __( 'Open this outbound link in a new window', 'wp-slimstat' ), ENT_QUOTES, 'UTF-8' ) . "' href='{$a_resource}'></a> {$a_resource}";
246
- }
247
- }
248
- else {
249
- $results[$i][ 'outbound_resource' ] = '';
250
- }
251
-
252
- if ( $current_user_can_delete ){
253
- $delete_row = "<a class='slimstat-delete-entry slimstat-font-cancel' data-pageview-id='{$results[$i]['id']}' title='".htmlentities(__('Delete this entry from the database','wp-slimstat'), ENT_QUOTES, 'UTF-8')."' href='#'></a>";
254
- }
255
-
256
- // Login / Logout Event
257
- $login_logout = '';
258
- if ( strpos( $results[ $i ][ 'notes' ], 'loggedin:' ) !== false ) {
259
- $exploded_notes = explode( ';', $results[ $i ][ 'notes' ] );
260
- foreach ( $exploded_notes as $a_note ) {
261
- if ( strpos( $a_note, 'loggedin:' ) === false ) {
262
- continue;
263
- }
264
-
265
- $login_logout .= "<i class='slimstat-font-user-plus spaced' title='" . __( 'User Logged In', 'wp-slimstat' ) . "'></i> " . str_replace( 'loggedin:', '', $a_note );
266
- }
267
- }
268
- if ( strpos( $results[ $i ][ 'notes' ], 'loggedout:' ) !== false ) {
269
- $exploded_notes = explode( ';', $results[ $i ][ 'notes' ] );
270
- foreach ( $exploded_notes as $a_note ) {
271
- if ( strpos( $a_note, 'loggedout:' ) === false ) {
272
- continue;
273
- }
274
-
275
- $login_logout .= "<i class='slimstat-font-user-times spaced' title='" . __( 'User Logged Out', 'wp-slimstat' ) . "'></i> " . str_replace( 'loggedout:', '', $a_note );
276
- }
277
- }
278
- }
279
- else {
280
- $results[$i]['referer'] = $results[$i][ 'outbound_resource' ] = $results[$i][ 'content_type' ] = '';
281
- }
282
-
283
- $row_output = "<p>{$results[$i]['resource']} <span class='details'>$time_on_page $login_logout {$results[$i]['searchterms']} {$results[$i]['referer']} {$results[$i]['outbound_resource']} {$results[$i]['content_type']} $performance $date_time {$notes} {$delete_row}</span></p>";
284
-
285
- // Strip all the filter links, if this information is shown on the frontend
286
- if ( !is_admin() ) {
287
- $row_output = preg_replace('/<a (.*?)>(.*?)<\/a>/', "\\2", $row_output);
288
- }
289
-
290
- echo $row_output;
291
- }
292
-
293
- // Pagination
294
- if ( $count_page_results > 20 ) {
295
- echo wp_slimstat_reports::report_pagination( $count_page_results, $count_all_results, true, wp_slimstat::$settings[ 'number_results_raw_data' ] );
296
- }
297
- }
1
+ <?php
2
+ // Avoid direct access to this piece of code
3
+ if ( !function_exists( 'add_action' ) ) {
4
+ exit(0);
5
+ }
6
+
7
+ if ( wp_slimstat::$settings[ 'async_load' ] == 'on' && ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
8
+ return '';
9
+ }
10
+
11
+ $is_dashboard = empty( $_REQUEST[ 'page' ] ) || $_REQUEST[ 'page' ] != 'slimview1';
12
+
13
+ // Available icons
14
+ $supported_browser_icons = array('Android','Anonymouse','Baiduspider','BlackBerry','BingBot','CFNetwork','Chrome','Chromium','Default Browser','Edge','Exabot/BiggerBetter','FacebookExternalHit','FeedBurner','Feedfetcher-Google','Firefox','Internet Archive','Googlebot','Google Bot','Google Feedfetcher','Google Web Preview','IE','IEMobile','iPad','iPhone','iPod Touch','Maxthon','Mediapartners-Google','Microsoft-WebDAV','msnbot','Mozilla','NewsGatorOnline','Netscape','Nokia','Opera','Opera Mini','Opera Mobi','Pingdom','Python','PycURL','Safari','W3C_Validator','WordPress','Yahoo! Slurp','YandexBot');
15
+ $supported_os_icons = array('android','blackberry os','cellos','chromeos','ios','iphone osx','java','linux','macosx','rim os','symbianos','win7','win8','win8.1','win10','winphone7','winphone7.5','winphone8','winphone8.1','winvista','winxp','unknown');
16
+ $supported_browser_types = array(__('Human','wp-slimstat'),__('Bot/Crawler','wp-slimstat'),__('Mobile Device','wp-slimstat'),__('Syndication Reader','wp-slimstat'));
17
+
18
+ $plugin_url = plugins_url('', dirname(__FILE__));
19
+
20
+ // Get the data
21
+ wp_slimstat_db::$debug_message = '';
22
+ $all_results = wp_slimstat_db::get_recent( wp_slimstat_reports::$reports_info[ 'slim_p7_02' ][ 'callback_args' ] );
23
+ $results = array_slice(
24
+ $all_results,
25
+ wp_slimstat_db::$filters_normalized[ 'misc' ][ 'start_from' ],
26
+ wp_slimstat::$settings[ 'number_results_raw_data' ]
27
+ );
28
+
29
+ $count_all_results = count( $all_results );
30
+ $count_page_results = count( $results );
31
+
32
+ // Echo the debug message
33
+ echo wp_slimstat_db::$debug_message;
34
+
35
+ // Return the results if we are not echoing them (export, email, etc)
36
+ if ( isset( $_args[ 'echo' ] ) && $_args[ 'echo' ] === false ) {
37
+
38
+ // Massage the data before returning it
39
+ if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'on' ) {
40
+ for ( $i = 0; $i < $count_page_results; $i++ ) {
41
+ $gethostbyaddr = gethostbyaddr( $results[ $i ][ 'ip' ] );
42
+ if ( $gethostbyaddr != $host_by_ip && !empty( $gethostbyaddr ) ) {
43
+ $results[ $i ][ 'ip' ] .= ', ' . $gethostbyaddr;
44
+ }
45
+ }
46
+ }
47
+
48
+ return $results;
49
+ }
50
+
51
+ if ($count_page_results == 0){
52
+ echo '<p class="nodata">'.__('No data to display','wp-slimstat').'</p>';
53
+ }
54
+ else {
55
+
56
+ // Pagination
57
+ echo wp_slimstat_reports::report_pagination( $count_page_results, $count_all_results, true, wp_slimstat::$settings[ 'number_results_raw_data' ] );
58
+
59
+ // Show delete button? (only those who can access the settings can see it)
60
+ $current_user_can_delete = ( current_user_can( wp_slimstat::$settings[ 'capability_can_admin' ] ) && !is_network_admin() );
61
+ $delete_row = '';
62
+
63
+ // Loop through the results
64
+ for ( $i=0; $i < $count_page_results; $i++ ) {
65
+ $host_by_ip = $results[ $i ][ 'ip' ];
66
+ if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'on' ) {
67
+ $gethostbyaddr = gethostbyaddr( $results[ $i ][ 'ip' ] );
68
+ if ( $gethostbyaddr != $host_by_ip && !empty( $gethostbyaddr ) ) {
69
+ $host_by_ip .= ', ' . $gethostbyaddr;
70
+ }
71
+ }
72
+
73
+ $date_time = "<i class='spaced slimstat-font-clock slimstat-tooltip-trigger' title='".__( 'Date and Time', 'wp-slimstat' )."'></i> " . date_i18n( wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ], $results[ $i ][ 'dt' ], true );
74
+
75
+ // Print visit header?
76
+ if ( $i == 0 || $results[ $i - 1 ][ 'visit_id' ] != $results[ $i ][ 'visit_id' ] || $results[ $i - 1 ][ 'ip' ] != $results[ $i ][ 'ip' ] || $results[ $i - 1 ][ 'browser' ] != $results[ $i ][ 'browser' ] || $results[ $i - 1 ][ 'platform' ] != $results[ $i ][ 'platform' ] || $results[ $i - 1 ][ 'username' ] != $results[ $i ][ 'username' ] ) {
77
+
78
+ // Color-coded headers
79
+ $highlight_row = !empty($results[$i]['searchterms'])?' is-search-engine':(($results[$i]['browser_type'] != 1)?' is-direct':'');
80
+
81
+ // Country
82
+ $country_filtered = '';
83
+ if ( !empty( $results[ $i ][ 'country' ] ) && $results[ $i ][ 'country' ] != 'xx' ) {
84
+ $country_filtered = "<a class='slimstat-filter-link inline-icon' href='" . wp_slimstat_reports::fs_url( 'country equals ' . $results[ $i ][ 'country' ] ) . "'><img class='slimstat-tooltip-trigger' src='$plugin_url/images/flags/{$results[$i]['country']}.png' width='16' height='16' title='" . __( 'c-' . $results[ $i ][ 'country' ], 'wp-slimstat' ) . "'></a>";
85
+ }
86
+
87
+ // City, if tracked
88
+ $city_filtered = '';
89
+ if ( !empty( $results[ $i ][ 'city' ] ) ) {
90
+ $city_filtered = "<a class='slimstat-filter-link' href='" . wp_slimstat_reports::fs_url( 'city equals ' . $results[ $i ][ 'city' ] ) . "'>{$results[ $i ][ 'city' ]}</a>";
91
+ }
92
+
93
+ // Browser
94
+ if ($results[$i]['browser_version'] == 0) $results[$i]['browser_version'] = '';
95
+ $browser_title = ( wp_slimstat::$settings[ 'show_complete_user_agent_tooltip' ] != 'on' ) ? "{$results[ $i ][ 'browser' ]} {$results[ $i ][ 'browser_version' ]}" : $results[ $i ][ 'user_agent' ];
96
+ $browser_icon = $plugin_url.'/images/browsers/other-browsers-and-os.png';
97
+ if (in_array($results[$i]['browser'], $supported_browser_icons)){
98
+ $browser_icon = $plugin_url.'/images/browsers/'.sanitize_title($results[$i]['browser']).'.png';
99
+ }
100
+ $browser_filtered = "<a class='slimstat-filter-link inline-icon' href='".wp_slimstat_reports::fs_url('browser equals '.$results[$i]['browser'])."'><img class='slimstat-tooltip-trigger' src='$browser_icon' width='16' height='16' title='{$browser_title}'></a>";
101
+
102
+ // Platform
103
+ $platform_icon = $plugin_url.'/images/browsers/other-browsers-and-os.png';
104
+ if (in_array(strtolower($results[$i]['platform']), $supported_os_icons)){
105
+ $platform_icon = $plugin_url.'/images/platforms/'.sanitize_title($results[$i]['platform']).'.png';
106
+ }
107
+ $platform_filtered = "<a class='slimstat-filter-link inline-icon' href='".wp_slimstat_reports::fs_url('platform equals '.$results[$i]['platform'])."'><img class='slimstat-tooltip-trigger' src='$platform_icon' width='16' height='16' title='" . __( $results[ $i ][ 'platform' ], 'wp-slimstat' ) . "'></a>";
108
+
109
+ // Browser Type
110
+ $browser_type_filtered = '';
111
+ if ($results[$i]['browser_type'] != 0){
112
+ $browser_type_filtered = "<a class='slimstat-filter-link inline-icon' href='".wp_slimstat_reports::fs_url('browser_type equals '.$results[$i]['browser_type'])."'><img class='slimstat-tooltip-trigger' src='$plugin_url/images/browsers/type{$results[$i]['browser_type']}.png' width='16' height='16' title='{$supported_browser_types[$results[$i]['browser_type']]}'></a>";
113
+ }
114
+
115
+ // IP Address and user
116
+ if (empty($results[$i]['username'])){
117
+ $ip_address = "<a class='slimstat-filter-link' href='".wp_slimstat_reports::fs_url('ip equals '.$results[$i]['ip'])."'>$host_by_ip</a>";
118
+ }
119
+ else{
120
+ $display_user_name = $results[ $i ][ 'username' ];
121
+ if ( wp_slimstat::$settings[ 'show_display_name' ] == 'on' && strpos( $results[ $i ][ 'notes' ], 'user:' ) !== false ) {
122
+ $display_real_name = get_user_by('login', $results[$i]['username']);
123
+ if (is_object($display_real_name)) $display_user_name = $display_real_name->display_name;
124
+ }
125
+ $ip_address = "<a class='slimstat-filter-link' href='".wp_slimstat_reports::fs_url('username equals '.$results[$i]['username'])."'>{$display_user_name}</a>";
126
+ $ip_address .= " <a class='slimstat-filter-link' href='".wp_slimstat_reports::fs_url('ip equals '.$results[$i]['ip'])."'>($host_by_ip)</a>";
127
+ $highlight_row = (strpos( $results[$i]['notes'], 'user:') !== false)?' is-known-user':' is-known-visitor';
128
+
129
+ }
130
+
131
+ $whois_pin = '';
132
+ if ( is_admin() && !empty( wp_slimstat::$settings[ 'ip_lookup_service' ] ) && !wp_slimstat::is_local_ip_address( $results[ $i ][ 'ip' ] ) ) {
133
+ $whois_pin = "<a class='slimstat-font-location-1 whois' href='" . wp_slimstat::$settings[ 'ip_lookup_service' ] . "{$results[ $i ][ 'ip' ]}' target='_blank' title='WHOIS: {$results[ $i ][ 'ip' ]}'></a>";
134
+ }
135
+
136
+ // Originating IP Address
137
+ $other_ip_address = intval( $results[ $i ][ 'other_ip' ] );
138
+ if ( !empty( $other_ip_address ) ) {
139
+ $other_ip_address = "<a class='slimstat-filter-link' href='" . wp_slimstat_reports::fs_url( 'other_ip equals '. $results[ $i ][ 'other_ip' ] ) . "'>(" . __( 'Originating IP', 'wp-slimstat' ) . ": {$results[$i]['other_ip']})</a>";
140
+ }
141
+ else {
142
+ $other_ip_address = '';
143
+ }
144
+
145
+ // Plugins
146
+ $plugins = '';
147
+ if (!empty($results[$i]['plugins'])){
148
+ $results[$i]['plugins'] = explode(',', $results[$i]['plugins']);
149
+ foreach($results[$i]['plugins'] as $a_plugin){
150
+ $a_plugin = trim($a_plugin);
151
+ $plugins .= "<a class='slimstat-filter-link inline-icon' href='" . wp_slimstat_reports::fs_url( 'plugins contains ' . $a_plugin ) . "'><img class='slimstat-tooltip-trigger' src='$plugin_url/images/plugins/$a_plugin.png' width='16' height='16' title='" . __( $a_plugin, 'wp-slimstat' ) . "'></a> ";
152
+ }
153
+ }
154
+
155
+ // Screen Resolution
156
+ $screen_resolution = '';
157
+ if ( !empty( $results[ $i ][ 'screen_width' ] ) && !empty( $results[ $i ][ 'screen_height' ] ) ) {
158
+ $screen_resolution = "<span class='pageview-screenres'>{$results[ $i ][ 'screen_width' ]}x{$results[ $i ][ 'screen_height' ]}</span>";
159
+ }
160
+
161
+ $row_output = "<p class='header$highlight_row'>$browser_filtered $platform_filtered $browser_type_filtered $country_filtered $whois_pin $city_filtered $ip_address $other_ip_address <span class='plugins'>$plugins</span> $screen_resolution</p>";
162
+
163
+ // Strip all the filter links, if this information is shown on the frontend
164
+ if ( !is_admin() ) {
165
+ $row_output = preg_replace('/<a (.*?)>(.*?)<\/a>/', "\\2", $row_output);
166
+ }
167
+
168
+ echo $row_output;
169
+ }
170
+
171
+ // Permalink: find post title, if available
172
+ $parse_url = parse_url(get_site_url(empty($results[$i]['blog_id'])?1:$results[$i]['blog_id']));
173
+ $base_host = $parse_url['host'];
174
+ $base_url = '';
175
+
176
+ if ( !empty( $results[ $i ][ 'resource' ] ) ) {
177
+ if (!empty($results[$i]['blog_id'])){
178
+ $base_url = $parse_url['scheme'].'://'.$base_host;
179
+ }
180
+ $results[$i]['resource'] = "<a class='slimstat-font-logout slimstat-tooltip-trigger' target='_blank' title='".htmlentities(__('Open this URL in a new window','wp-slimstat'), ENT_QUOTES, 'UTF-8')."' href='".$base_url.htmlentities($results[$i]['resource'], ENT_QUOTES, 'UTF-8')."'></a> $base_url<a class='slimstat-filter-link' href='".wp_slimstat_reports::fs_url('resource equals ' . htmlentities($results[$i]['resource'], ENT_QUOTES, 'UTF-8') ) . "'>".wp_slimstat_reports::get_resource_title( $results[$i][ 'resource' ], $results[$i][ 'category' ] ).'</a>';
181
+ }
182
+ else {
183
+ if ( !empty( $results[$i][ 'notes' ] ) ) {
184
+ $exploded_notes = explode( ';', $results[$i][ 'notes' ] );
185
+ foreach ( $exploded_notes as $a_note ) {
186
+ if ( strpos( $a_note, 'results:') !== false ) {
187
+ $search_terms_info = $results[ $i ][ 'searchterms' ] . ' (' . $a_note . ')';
188
+ break;
189
+ }
190
+ }
191
+ }
192
+ $results[$i]['resource'] = __('Local search results page','wp-slimstat');
193
+ }
194
+
195
+ if ( empty( $search_terms_info ) ) {
196
+ $search_terms_info = wp_slimstat_reports::get_search_terms_info( $results[ $i ][ 'searchterms' ], $results[ $i ][ 'referer' ] );
197
+ }
198
+
199
+ // Search Terms, with link to original SERP, and Outbound Resource
200
+ if ( !empty( $search_terms_info ) ) {
201
+ $results[$i]['searchterms'] = "<i class='spaced slimstat-font-search' title='" . __( 'Search Terms', 'wp-slimstat' ) . "'></i> $search_terms_info";
202
+ }
203
+ else {
204
+ $results[$i]['searchterms'] = '';
205
+ }
206
+
207
+ // Let's reset this variable for the next item
208
+ $search_terms_info = '';
209
+
210
+ // Server Latency and Page Speed
211
+ $performance = '';
212
+ if ( !$is_dashboard && ( !empty( $results[ $i ][ 'server_latency' ] ) || !empty( $results[ $i ][ 'page_performance' ] ) ) ) {
213
+ $performance = "<i class='slimstat-font-gauge spaced slimstat-tooltip-trigger' title='".__('Server Latency and Page Speed in milliseconds','wp-slimstat')."'></i> ".__('SL','wp-slimstat').": {$results[$i]['server_latency']} / ".__('PS','wp-slimstat').": {$results[$i]['page_performance']}";
214
+ }
215
+
216
+ // Time on page
217
+ $time_on_page = '';
218
+ if ( !$is_dashboard && !empty( $results[ $i ][ 'dt_out' ] ) ) {
219
+ $duration = $results[ $i ][ 'dt_out' ] - $results[ $i ][ 'dt' ];
220
+ $time_on_page = "<i class='slimstat-font-stopwatch spaced slimstat-tooltip-trigger' title='" . __( 'Time spent on this page', 'wp-slimstat' ) . "'></i> " . date( ( $duration > 3599 ? 'H:i:s' : 'i:s' ), $duration );
221
+ }
222
+
223
+ // Pageview Notes
224
+ $notes = '';
225
+ if ( is_admin() && !empty( $results[ $i ][ 'notes' ] ) ) {
226
+ $notes = str_replace(array(';', ':'), array('<br/>', ': '), $results[$i]['notes']);
227
+ $notes = "<i class='slimstat-font-edit slimstat-tooltip-trigger'><b class='slimstat-tooltip-content'>{$notes}</b></i>";
228
+ }
229
+
230
+ // Avoid XSS attacks through the referer URL
231
+ $results[ $i ] [ 'referer' ] = str_replace( array( '<', '>', '%22', '%27', '\'', '"', '%3C', '%3E' ), array( '&lt;', '&gt;', '', '', '', '', '&lt;', '&gt;' ), urldecode( $results[ $i ] [ 'referer' ] ) );
232
+
233
+ $login_logout = '';
234
+ if ( !$is_dashboard ) {
235
+ $domain = parse_url( $results[ $i ] [ 'referer' ] );
236
+ $domain = !empty( $domain[ 'host' ] ) ? $domain[ 'host' ] : __( 'Invalid Referrer', 'wp-slimstat' );
237
+ $results[$i][ 'referer' ] = (!empty($results[$i]['referer']) && empty($results[$i]['searchterms']))?"<a class='spaced slimstat-font-login slimstat-tooltip-trigger' target='_blank' title='".htmlentities(__('Open this referrer in a new window','wp-slimstat'), ENT_QUOTES, 'UTF-8')."' href='{$results[$i]['referer']}'></a> $domain":'';
238
+ $results[$i][ 'content_type' ] = !empty($results[$i]['content_type'])?"<i class='spaced slimstat-font-doc slimstat-tooltip-trigger' title='".__('Content Type','wp-slimstat')."'></i> <a class='slimstat-filter-link' href='".wp_slimstat_reports::fs_url('content_type equals '.$results[$i]['content_type'])."'>{$results[$i]['content_type']}</a> ":'';
239
+
240
+ // The Outbound Links field might contain more than one link
241
+ if ( !empty( $results[ $i ][ 'outbound_resource' ] ) ) {
242
+ $exploded_outbound_resources = explode( ';;;', $results[ $i ][ 'outbound_resource' ] );
243
+ $results[$i][ 'outbound_resource' ] = '';
244
+ foreach ( $exploded_outbound_resources as $a_resource ) {
245
+ $results[ $i ][ 'outbound_resource' ] .= "<a class='inline-icon spaced slimstat-font-logout slimstat-tooltip-trigger' target='_blank' title='".htmlentities( __( 'Open this outbound link in a new window', 'wp-slimstat' ), ENT_QUOTES, 'UTF-8' ) . "' href='{$a_resource}'></a> {$a_resource}";
246
+ }
247
+ }
248
+ else {
249
+ $results[$i][ 'outbound_resource' ] = '';
250
+ }
251
+
252
+ if ( $current_user_can_delete ){
253
+ $delete_row = "<a class='slimstat-delete-entry slimstat-font-cancel slimstat-tooltip-trigger' data-pageview-id='{$results[$i]['id']}' title='".htmlentities(__('Delete this entry from the database','wp-slimstat'), ENT_QUOTES, 'UTF-8')."' href='#'></a>";
254
+ }
255
+
256
+ // Login / Logout Event
257
+ $login_logout = '';
258
+ if ( strpos( $results[ $i ][ 'notes' ], 'loggedin:' ) !== false ) {
259
+ $exploded_notes = explode( ';', $results[ $i ][ 'notes' ] );
260
+ foreach ( $exploded_notes as $a_note ) {
261
+ if ( strpos( $a_note, 'loggedin:' ) === false ) {
262
+ continue;
263
+ }
264
+
265
+ $login_logout .= "<i class='slimstat-font-user-plus spaced slimstat-tooltip-trigger' title='" . __( 'User Logged In', 'wp-slimstat' ) . "'></i> " . str_replace( 'loggedin:', '', $a_note );
266
+ }
267
+ }
268
+ if ( strpos( $results[ $i ][ 'notes' ], 'loggedout:' ) !== false ) {
269
+ $exploded_notes = explode( ';', $results[ $i ][ 'notes' ] );
270
+ foreach ( $exploded_notes as $a_note ) {
271
+ if ( strpos( $a_note, 'loggedout:' ) === false ) {
272
+ continue;
273
+ }
274
+
275
+ $login_logout .= "<i class='slimstat-font-user-times spaced slimstat-tooltip-trigger' title='" . __( 'User Logged Out', 'wp-slimstat' ) . "'></i> " . str_replace( 'loggedout:', '', $a_note );
276
+ }
277
+ }
278
+ }
279
+ else {
280
+ $results[$i]['referer'] = $results[$i][ 'outbound_resource' ] = $results[$i][ 'content_type' ] = '';
281
+ }
282
+
283
+ $row_output = "<p>{$results[$i]['resource']} <span class='details'>$time_on_page $login_logout {$results[$i]['searchterms']} {$results[$i]['referer']} {$results[$i]['outbound_resource']} {$results[$i]['content_type']} $performance $date_time {$notes} {$delete_row}</span></p>";
284
+
285
+ // Strip all the filter links, if this information is shown on the frontend
286
+ if ( !is_admin() ) {
287
+ $row_output = preg_replace('/<a (.*?)>(.*?)<\/a>/', "\\2", $row_output);
288
+ }
289
+
290
+ echo $row_output;
291
+ }
292
+
293
+ // Pagination
294
+ if ( $count_page_results > 20 ) {
295
+ echo wp_slimstat_reports::report_pagination( $count_page_results, $count_all_results, true, wp_slimstat::$settings[ 'number_results_raw_data' ] );
296
+ }
297
+ }
admin/view/wp-slimstat-db.php CHANGED
@@ -1,1099 +1,1007 @@
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 $operator_names = array();
8
- public static $filters_normalized = array();
9
-
10
- // Number and date formats
11
- public static $formats = array( 'decimal' => ',', 'thousand' => '.' );
12
-
13
- // Structure that maps filters to SQL information (table names, clauses, lookup tables, etc)
14
- public static $sql_where = array( 'columns' => '', 'time_range' => '' );
15
-
16
- // Filters that are not visible in the dropdown
17
- public static $all_columns_names = array();
18
-
19
- // Debug message
20
- public static $debug_message = '';
21
-
22
- /*
23
- * Sets the filters and other structures needed to store the data retrieved from the DB
24
- */
25
- public static function init( $_filters = '' ) {
26
- // Decimal and thousand separators
27
- if ( wp_slimstat::$settings[ 'use_european_separators' ] == 'no' ){
28
- self::$formats[ 'decimal' ] = '.';
29
- self::$formats[ 'thousand' ] = ',';
30
- }
31
-
32
- // List of supported filters and their friendly names
33
- self::$columns_names = array(
34
- 'browser' => array( __( 'Browser', 'wp-slimstat' ), 'varchar' ),
35
- 'country' => array( __( 'Country Code', 'wp-slimstat' ), 'varchar' ),
36
- 'ip' => array( __( 'IP Address', 'wp-slimstat' ), 'varchar' ),
37
- 'searchterms' => array( __( 'Search Terms', 'wp-slimstat' ), 'varchar' ),
38
- 'language' => array( __( 'Language Code', 'wp-slimstat' ), 'varchar' ),
39
- 'platform' => array( __( 'Operating System', 'wp-slimstat' ), 'varchar' ),
40
- 'resource' => array( __( 'Permalink', 'wp-slimstat' ), 'varchar' ),
41
- 'referer' => array( __( 'Referer', 'wp-slimstat' ), 'varchar' ),
42
- 'username' => array( __( 'Visitor\'s Username', 'wp-slimstat' ), 'varchar' ),
43
- 'outbound_resource' => array( __( 'Outbound Link', 'wp-slimstat' ), 'varchar' ),
44
- 'page_performance' => array( __( 'Page Speed', 'wp-slimstat' ), 'int' ),
45
- 'no_filter_selected_2' => array( '', 'none' ),
46
- 'no_filter_selected_3' => array( __( '-- Advanced filters --', 'wp-slimstat' ), 'none' ),
47
- 'plugins' => array( __( 'Browser Capabilities', 'wp-slimstat' ), 'varchar' ),
48
- 'browser_version' => array( __( 'Browser Version', 'wp-slimstat' ), 'varchar' ),
49
- 'browser_type' => array( __( 'Browser Type', 'wp-slimstat' ), 'int' ),
50
- 'user_agent' => array( __( 'User Agent', 'wp-slimstat' ), 'varchar' ),
51
- 'city' => array( __( 'City', 'wp-slimstat' ), 'varchar' ),
52
- 'location' => array( __( 'Coordinates', 'wp-slimstat' ), 'varchar' ),
53
- 'notes' => array( __( 'Annotations', 'wp-slimstat' ), 'varchar' ),
54
- 'server_latency' => array( __( 'Server Latency', 'wp-slimstat' ), 'int' ),
55
- 'author' => array( __( 'Post Author', 'wp-slimstat' ), 'varchar' ),
56
- 'category' => array( __( 'Post Category ID', 'wp-slimstat' ), 'varchar' ),
57
- 'other_ip' => array( __( 'Originating IP', 'wp-slimstat' ), 'varchar' ),
58
- 'content_type' => array( __( 'Resource Content Type', 'wp-slimstat' ), 'varchar' ),
59
- 'content_id' => array( __( 'Resource ID', 'wp-slimstat' ), 'int' ),
60
- 'screen_width' => array( __( 'Screen Width', 'wp-slimstat' ), 'int' ),
61
- 'screen_height' => array( __( 'Screen Height', 'wp-slimstat' ), 'int' ),
62
- 'resolution' => array( __( 'Viewport Size', 'wp-slimstat' ), 'varchar' ),
63
- 'visit_id' => array( __( 'Visit ID', 'wp-slimstat' ), 'int' )
64
- );
65
-
66
- if ( wp_slimstat::$settings[ 'geolocation_country' ] == 'on' ) {
67
- unset( self::$columns_names[ 'city' ] );
68
- unset( self::$columns_names[ 'location' ] );
69
- }
70
-
71
- // List of supported filters and their friendly names
72
- self::$operator_names = array(
73
- 'equals' => __( 'equals', 'wp-slimstat' ),
74
- 'is_not_equal_to' => __( 'is not equal to', 'wp-slimstat' ),
75
- 'contains' => __( 'contains', 'wp-slimstat' ),
76
- 'includes_in_set' => __( 'is included in', 'wp-slimstat' ),
77
- 'does_not_contain' => __( 'does not contain', 'wp-slimstat' ),
78
- 'starts_with' => __( 'starts with', 'wp-slimstat' ),
79
- 'ends_with' => __( 'ends with', 'wp-slimstat' ),
80
- 'sounds_like' => __( 'sounds like', 'wp-slimstat' ),
81
- 'is_greater_than' => __( 'is greater than', 'wp-slimstat' ),
82
- 'is_less_than' => __( 'is less than', 'wp-slimstat' ),
83
- 'between' => __( 'is between (x,y)', 'wp-slimstat' ),
84
- 'matches' => __( 'matches', 'wp-slimstat' ),
85
- 'does_not_match' => __( 'does not match', 'wp-slimstat' ),
86
- 'is_empty' => __( 'is empty', 'wp-slimstat' ),
87
- 'is_not_empty' => __( 'is not empty', 'wp-slimstat' ),
88
- );
89
-
90
- // The following filters will not be displayed in the dropdown
91
- self::$all_columns_names = array_merge( array(
92
-
93
- // Date and Time
94
- 'minute' => array( __( 'Minute', 'wp-slimstat' ), 'int' ),
95
- 'hour' => array( __( 'Hour', 'wp-slimstat' ), 'int' ),
96
- 'day' => array( __( 'Day', 'wp-slimstat' ), 'int' ),
97
- 'month' => array( __( 'Month', 'wp-slimstat' ), 'int' ),
98
- 'year' => array( __( 'Year', 'wp-slimstat' ), 'int' ),
99
- 'interval_direction' => array( __( '+/-', 'wp-slimstat' ), 'int' ),
100
- 'interval' => array( __( 'days', 'wp-slimstat' ), 'int' ),
101
- 'interval_hours' => array( __( 'hours', 'wp-slimstat' ), 'int' ),
102
- 'interval_minutes' => array( __( 'minutes', 'wp-slimstat' ), 'int' ),
103
- 'dt' => array( __( 'Timestamp', 'wp-slimstat' ), 'int' ),
104
- 'dt_out' => array( __( 'Exit Timestamp', 'wp-slimstat' ), 'int' ),
105
-
106
- // Other columns
107
- 'language_calculated' => array( __( 'Language', 'wp-slimstat' ), 'varchar' ),
108
- 'platform_calculated' => array( __( 'Operating System', 'wp-slimstat' ), 'varchar' ),
109
- 'resource_calculated' => array( __( 'Permalink', 'wp-slimstat' ), 'varchar' ),
110
- 'referer_calculated' => array( __( 'Referer', 'wp-slimstat' ), 'varchar' ),
111
- 'metric' => array( __( 'Metric', 'wp-slimstat' ), 'varchar' ),
112
- 'value' => array( __( 'Value', 'wp-slimstat' ), 'varchar' ),
113
- 'counthits' => array( __( 'Hits', 'wp-slimstat' ), 'int' ),
114
- 'percentage' => array( __( 'Percentage', 'wp-slimstat' ), 'int' ),
115
- 'tooltip' => array( __( 'Notes', 'wp-slimstat' ), 'varchar' ),
116
- 'details' => array( __( 'Notes', 'wp-slimstat' ), 'varchar' ),
117
-
118
- // Events
119
- 'event_id' => array( __( 'Event ID', 'wp-slimstat' ), 'int' ),
120
- 'type' => array( __( 'Type', 'wp-slimstat' ), 'int' ),
121
- 'event_description' => array( __( 'Event Description', 'wp-slimstat' ), 'varchar' ),
122
- 'position' => array( __( 'Event Coordinates', 'wp-slimstat' ), 'int' ),
123
-
124
- 'limit_results' => array( __( 'Max Results', 'wp-slimstat' ), 'int' ),
125
- 'start_from' => array( __( 'Offset', 'wp-slimstat' ), 'int' ),
126
-
127
- // Misc Filters
128
- 'strtotime' => array( 0, 'int' )
129
- ), self::$columns_names );
130
-
131
- // Allow third party plugins to add even more column names to the array
132
- self::$all_columns_names = apply_filters( 'slimstat_column_names', self::$all_columns_names );
133
-
134
- // Filters use the following format: browser equals Firefox&&&country contains gb
135
- $filters_array = array();
136
-
137
- // Filters are set via javascript as hidden fields and submitted as a POST request. They override anything passed through the regular input fields
138
- if ( !empty( $_POST[ 'fs' ] ) && is_array( $_POST[ 'fs' ] ) ) {
139
- foreach( $_POST[ 'fs' ] as $a_request_filter_name => $a_request_filter_value ) {
140
- $filters_array[ htmlspecialchars( $a_request_filter_name ) ] = "$a_request_filter_name $a_request_filter_value";
141
- }
142
- }
143
-
144
- // Date filters (input fields)
145
- foreach ( array( 'minute', 'hour', 'day', 'month', 'year', 'interval_direction', 'interval', 'interval_hours', 'interval_minutes' ) as $a_date_time_filter_name ) {
146
- if ( !empty( $_POST[ $a_date_time_filter_name ] ) ) {
147
- $filters_array[ $a_date_time_filter_name ] = "$a_date_time_filter_name equals " . intval( $_POST[ $a_date_time_filter_name ] );
148
- }
149
- }
150
-
151
- // Fields and drop downs
152
- if ( !empty( $_POST[ 'f' ] ) && !empty( $_POST[ 'o' ] ) ) {
153
- $filters_array[ htmlspecialchars( $_POST[ 'f' ] ) ] = "{$_POST[ 'f' ]} {$_POST[ 'o' ]} " . ( isset( $_POST[ 'v' ] ) ? $_POST[ 'v' ] : '' );
154
- }
155
-
156
- // Filters set via the plugin options
157
- if ( wp_slimstat::$settings[ 'restrict_authors_view' ] == 'on' && !current_user_can( 'manage_options' ) && !empty( $GLOBALS[ 'current_user' ]->user_login ) ) {
158
- $filters_array[ 'author' ] = 'author equals ' . $GLOBALS[ 'current_user' ]->user_login;
159
- }
160
-
161
- if ( !empty( $filters_array ) ) {
162
- $filters_raw = implode( '&&&', $filters_array );
163
- }
164
-
165
- // Filters are defined as: browser equals Chrome&&&country starts_with en
166
- if ( !isset( $filters_raw ) || !is_string( $filters_raw ) ) {
167
- $filters_raw = '';
168
- }
169
-
170
- if ( !empty( $_filters ) && is_string( $_filters ) ) {
171
- if ( !empty( $filters_raw ) ) {
172
- $filters_raw = empty( $filters_raw ) ? $_filters : $_filters . '&&&' . $filters_raw;
173
- }
174
- else {
175
- $filters_raw = $_filters;
176
- }
177
- }
178
-
179
- // Hook for the... filters
180
- $filters_raw = apply_filters( 'slimstat_db_pre_filters', $filters_raw );
181
-
182
- // Normalize the filters
183
- self::$filters_normalized = self::init_filters( $filters_raw );
184
- }
185
- // end init
186
-
187
- /**
188
- * Builds the array of WHERE clauses to be used later in our SQL queries
189
- */
190
- protected static function _get_sql_where( $_filters_normalized = array(), $_slim_stats_table_alias = '' ) {
191
- $sql_array = array();
192
-
193
- foreach ( $_filters_normalized as $a_filter_column => $a_filter_data ) {
194
- // Add-ons can set their own custom filters, which are ignored here
195
- if ( strpos( $a_filter_column, 'addon_' ) !== false ) {
196
- continue;
197
- }
198
-
199
- $sql_array[] = self::get_single_where_clause( $a_filter_column, $a_filter_data[ 0 ], $a_filter_data[ 1 ], $_slim_stats_table_alias );
200
- }
201
-
202
- // Flatten array
203
- if ( !empty( $sql_array ) ) {
204
- return implode( ' AND ', $sql_array );
205
- }
206
-
207
- return '';
208
- }
209
-
210
- public static function get_combined_where( $_where = '', $_column = '*', $_use_date_filters = true, $_slim_stats_table_alias = '' ) {
211
- $dt_with_alias = 'dt';
212
- if ( !empty( $_slim_stats_table_alias ) ) {
213
- $dt_with_alias = $_slim_stats_table_alias . '.' . $dt_with_alias;
214
- }
215
-
216
- $time_range_condition = '';
217
- if ( empty( $_where ) ) {
218
- if ( !empty( self::$filters_normalized[ 'columns' ] ) ) {
219
- $_where = self::_get_sql_where( self::$filters_normalized[ 'columns' ], $_slim_stats_table_alias );
220
-
221
- if ($_use_date_filters) {
222
- $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
223
- }
224
- }
225
- elseif ( $_use_date_filters ) {
226
- $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
227
- }
228
-
229
- // This could happen if we have custom filters (add-ons, third party tools)
230
- if ( empty( $_where ) ) {
231
- $_where = '1=1';
232
- }
233
- }
234
- else {
235
- if ( $_where != '1=1' && !empty( self::$filters_normalized[ 'columns' ] ) ) {
236
- $new_clause = self::_get_sql_where( self::$filters_normalized[ 'columns' ], $_slim_stats_table_alias );
237
-
238
- // This condition could be empty if it's related to a custom column
239
- if ( !empty( $new_clause ) ) {
240
- $_where .= ' AND ' . $new_clause;
241
- }
242
- }
243
- if ( $_use_date_filters ) {
244
- $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
245
- }
246
- }
247
-
248
- if ( !empty( $_where ) && !empty( $time_range_condition ) ) {
249
- $_where = "$_where AND $time_range_condition";
250
- }
251
- else {
252
- $_where = trim( "$_where $time_range_condition" );
253
- }
254
-
255
- if ( !empty( $_column ) && !empty( self::$columns_names[ $_column ] ) ) {
256
- $_column = str_replace( '_calculated', '', $_column );
257
- $column_with_alias = $_column;
258
- if ( !empty( $_slim_stats_table_alias ) ) {
259
- $column_with_alias = $_slim_stats_table_alias . '.' . $column_with_alias;
260
- }
261
-
262
- $filter_empty = "$column_with_alias " . ( ( self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NULL' : '= 0' );
263
- $filter_not_empty = "$column_with_alias " . ( ( self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NOT NULL' : '<> 0' );
264
-
265
- if ( strpos( $_where, $filter_empty ) === false && strpos( $_where, $filter_not_empty) === false) {
266
- $_where = "$filter_not_empty AND $_where";
267
- }
268
- }
269
-
270
- return $_where;
271
- }
272
-
273
- /**
274
- * Translates user-friendly operators into SQL conditions
275
- */
276
- public static function get_single_where_clause( $_column = 'id', $_operator = 'equals', $_value = '', $_slim_stats_table_alias = '' ) {
277
- $filter_empty = ( !empty( self::$columns_names[ $_column ] ) && self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NULL' : '= 0';
278
- $filter_not_empty = ( !empty( self::$columns_names[ $_column ] ) && self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NOT NULL' : '<> 0';
279
-
280
- $_column = str_replace( '_calculated', '', $_column );
281
-
282
- $column_with_alias = $_column;
283
- if ( !empty( $_slim_stats_table_alias ) ) {
284
- $column_with_alias = $_slim_stats_table_alias . '.' . $_column;
285
- }
286
-
287
- switch( $_column ) {
288
- case 'ip':
289
- case 'other_ip':
290
- $filter_empty = '= "0.0.0.0"';
291
- break;
292
- default:
293
- break;
294
- }
295
-
296
- $where = array( '', $_value );
297
- switch ( $_operator ) {
298
- case 'is_not_equal_to':
299
- $where[ 0 ] = "$column_with_alias <> %s";
300
- break;
301
-
302
- case 'contains':
303
- $where = array( "$column_with_alias LIKE %s", '%'.$_value.'%' );
304
- break;
305
-
306
- case 'includes_in_set':
307
- case 'included_in_set':
308
- $where[ 0 ] = "FIND_IN_SET($column_with_alias, %s) > 0";
309
- break;
310
-
311
- case 'does_not_contain':
312
- $where = array( "$column_with_alias NOT LIKE %s", '%'.$_value.'%' );
313
- break;
314
-
315
- case 'starts_with':
316
- $where = array( "$column_with_alias LIKE %s", $_value.'%' );
317
- break;
318
-
319
- case 'ends_with':
320
- $where = array( "$column_with_alias LIKE %s", '%'.$_value );
321
- break;
322
-
323
- case 'sounds_like':
324
- $where[ 0 ] = "SOUNDEX($column_with_alias) = SOUNDEX(%s)";
325
- break;
326
-
327
- case 'is_empty':
328
- $where = array( "$column_with_alias $filter_empty", '' );
329
- break;
330
-
331
- case 'is_not_empty':
332
- $where = array( "$column_with_alias $filter_not_empty", '' );
333
- break;
334
-
335
- case 'is_greater_than':
336
- $where[ 0 ] = "$column_with_alias > %d";
337
- break;
338
-
339
- case 'is_less_than':
340
- $where[ 0 ] = "$column_with_alias < %d";
341
- break;
342
-
343
- case 'between':
344
- $range = explode( ',', $_value );
345
- $where = array( "$column_with_alias BETWEEN %d AND %d", array( $range[ 0 ], $range[ 1 ] ) );
346
- break;
347
-
348
- case 'matches':
349
- $where[ 0 ] = "$column_with_alias REGEXP %s";
350
- break;
351
-
352
- case 'does_not_match':
353
- $where[ 0 ] = "$column_with_alias NOT REGEXP %s";
354
- break;
355
-
356
- default:
357
- $where[ 0 ] = "$column_with_alias = %s";
358
- break;
359
- }
360
-
361
- if ( isset( $where[ 1 ] ) && $where[ 1 ] != '' ) {
362
- return $GLOBALS[ 'wpdb' ]->prepare( $where[ 0 ], $where[ 1 ] );
363
- }
364
- else {
365
- return $where[ 0 ];
366
- }
367
- }
368
-
369
- public static function get_results( $_sql = '', $_select_no_aggregate_values = '', $_order_by = '', $_group_by = '', $_aggregate_values_add = '' ) {
370
- $_sql = apply_filters( 'slimstat_get_results_sql', $_sql, $_select_no_aggregate_values, $_order_by, $_group_by, $_aggregate_values_add );
371
-
372
- if ( wp_slimstat::$settings[ 'show_sql_debug' ] == 'on' ) {
373
- self::$debug_message .= "<p class='debug'>$_sql</p>";
374
- }
375
-
376
- return wp_slimstat::$wpdb->get_results( $_sql, ARRAY_A );
377
- }
378
-
379
- public static function get_var( $_sql = '', $_aggregate_value = '' ) {
380
- $_sql = apply_filters( 'slimstat_get_var_sql', $_sql, $_aggregate_value );
381
-
382
- if ( wp_slimstat::$settings[ 'show_sql_debug' ] == 'on' ) {
383
- self::$debug_message .= "<p class='debug'>$_sql</p>";
384
- }
385
-
386
- return wp_slimstat::$wpdb->get_var( $_sql );
387
- }
388
-
389
- public static function parse_filters( $_filters_raw ) {
390
- $filters_normalized = array(
391
- 'columns' => array(),
392
- 'date' => array()
393
- );
394
-
395
- if ( !empty( $_filters_raw ) ) {
396
- $matches = explode( '&&&', $_filters_raw );
397
-
398
- foreach( $matches as $idx => $a_match ) {
399
- preg_match( '/([^\s]+)\s([^\s]+)\s(.+)?/', urldecode( $a_match ), $a_filter );
400
-
401
- 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 ) ) {
402
- continue;
403
- }
404
-
405
- switch( $a_filter[ 1 ] ) {
406
- case 'strtotime':
407
- $custom_date = strtotime( $a_filter[ 3 ], date_i18n( 'U' ) );
408
-
409
- $filters_normalized[ 'date' ][ 'minute' ] = intval( date( 'i', $custom_date ) );
410
- $filters_normalized[ 'date' ][ 'hour' ] = intval( date( 'H', $custom_date ) );
411
- $filters_normalized[ 'date' ][ 'day' ] = intval( date( 'j', $custom_date ) );
412
- $filters_normalized[ 'date' ][ 'month' ] = intval( date( 'n', $custom_date ) );
413
- $filters_normalized[ 'date' ][ 'year' ] = intval( date( 'Y', $custom_date ) );
414
- break;
415
-
416
- case 'minute':
417
- case 'hour':
418
- case 'day':
419
- case 'month':
420
- case 'year':
421
- if ( is_numeric( $a_filter[ 3 ] ) ) {
422
- $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] = intval( $a_filter[ 3 ] );
423
- }
424
- else{
425
- // Try to apply strtotime to value
426
- switch( $a_filter[ 1 ] ) {
427
- case 'minute':
428
- $filters_normalized[ 'date' ][ 'minute' ] = intval( date( 'i', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
429
- break;
430
-
431
- case 'hour':
432
- $filters_normalized[ 'date' ][ 'hour' ] = intval( date( 'H', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
433
- break;
434
-
435
- case 'day':
436
- $filters_normalized[ 'date' ][ 'day' ] = intval( date( 'j', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
437
- break;
438
-
439
- case 'month':
440
- $filters_normalized[ 'date' ][ 'month' ] = intval( date( 'n', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
441
- break;
442
-
443
- case 'year':
444
- $filters_normalized[ 'date' ][ 'year' ] = intval( date( 'Y', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
445
- break;
446
-
447
- default:
448
- break;
449
- }
450
-
451
- if ( $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] === false ) {
452
- unset( $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] );
453
- }
454
- }
455
- break;
456
-
457
- case 'interval':
458
- case 'interval_hours':
459
- case 'interval_minutes':
460
- $intval_filter = intval( $a_filter[ 3 ] );
461
- $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] = abs( $intval_filter );
462
- if ( $intval_filter < 0 ) {
463
- $filters_normalized[ 'date' ][ 'interval_direction' ] = 1;
464
- }
465
- break;
466
-
467
- case 'interval_direction':
468
- $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] = in_array( $a_filter[ 3 ], array( 1, 2 ) ) ? $a_filter[ 3 ] : 1;
469
- break;
470
-
471
- case 'limit_results':
472
- case 'start_from':
473
- $filters_normalized[ 'misc' ][ $a_filter[ 1 ] ] = str_replace( '\\', '', htmlspecialchars_decode( $a_filter[ 3 ] ) );
474
- break;
475
-
476
- case 'content_id':
477
- if ( !empty( $a_filter[ 3 ] ) ) {
478
- $content_id = ( $a_filter[ 3 ] == 'current' && !empty( $GLOBALS[ 'post' ]->ID ) ) ? $GLOBALS[ 'post' ]->ID : $a_filter[ 3 ];
479
- $filters_normalized[ 'columns' ][ $a_filter[ 1 ] ] = array( $a_filter[ 2 ], $content_id );
480
- break;
481
- }
482
- // no break here: if value IS numeric, go to the default parser here below
483
-
484
- default:
485
- $filters_normalized[ 'columns' ][ $a_filter[ 1 ] ] = array( $a_filter[ 2 ], isset( $a_filter[ 3 ] ) ? str_replace( '\\', '', htmlspecialchars_decode( $a_filter[ 3 ] ) ) : '' );
486
- break;
487
- }
488
- }
489
- }
490
-
491
- return $filters_normalized;
492
- }
493
-
494
- public static function init_filters( $_filters_raw = '' ) {
495
- $filters_normalized = self::parse_filters( $_filters_raw );
496
-
497
- // Initialize default values
498
- if ( empty( $filters_normalized[ 'misc' ][ 'limit_results' ] ) ) {
499
- $filters_normalized[ 'misc' ][ 'limit_results' ] = wp_slimstat::$settings[ 'limit_results' ];
500
- }
501
- if ( empty( $filters_normalized[ 'misc' ][ 'start_from' ] ) ) {
502
- $filters_normalized[ 'misc' ][ 'start_from' ] = 0;
503
- }
504
-
505
- $filters_normalized[ 'utime' ] = array(
506
- 'start' => 0,
507
- 'end' => 0
508
- );
509
-
510
- // If the setting to use the last X days as default time span is enabled, we need to setup the "interval" variables
511
- if ( ( empty( wp_slimstat::$settings[ 'use_current_month_timespan' ] ) || wp_slimstat::$settings[ 'use_current_month_timespan' ] != 'on' ) ) {
512
- // Do not set the interval if another date filter has already been set
513
- $is_date_filter_empty = true;
514
- if ( !empty( $filters_normalized[ 'date' ] ) ) {
515
- $filters_to_check = array_diff( $filters_normalized[ 'date' ], array( 'interval_direction' => 1 ) );
516
- foreach( $filters_to_check as $a_filter ) {
517
- if ( isset( $a_filter ) ) {
518
- $is_date_filter_empty = false;
519
- break;
520
- }
521
- }
522
- }
523
-
524
- if ( $is_date_filter_empty ) {
525
- $filters_normalized[ 'date' ][ 'interval' ] = abs( wp_slimstat::$settings[ 'posts_column_day_interval' ] );
526
- $filters_normalized[ 'date' ][ 'interval_direction' ] = 1;
527
- }
528
- }
529
-
530
- // Temporarily disable any filters on date_i18n
531
- wp_slimstat::toggle_date_i18n_filters( false );
532
-
533
- // Let's calculate our time range, based on date filters
534
- if ( !isset( $filters_normalized[ 'date' ][ 'interval' ] ) && !isset( $filters_normalized[ 'date' ][ 'interval_hours' ] ) && !isset( $filters_normalized[ 'date' ][ 'interval_minutes' ] ) ) {
535
- if ( !empty( $filters_normalized[ 'date' ][ 'minute' ] ) ) {
536
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
537
- !empty( $filters_normalized[ 'date' ][ 'hour' ] ) ? $filters_normalized[ 'date' ][ 'hour' ] : 0,
538
- $filters_normalized[ 'date' ][ 'minute' ],
539
- 0,
540
- !empty( $filters_normalized[ 'date' ][ 'month' ] ) ? $filters_normalized[ 'date' ][ 'month' ] : date_i18n( 'n' ),
541
- !empty( $filters_normalized[ 'date' ][ 'day' ] ) ? $filters_normalized[ 'date' ][ 'day' ] : date_i18n( 'j' ),
542
- !empty( $filters_normalized[ 'date' ][ 'year' ] ) ? $filters_normalized[ 'date' ][ 'year' ] : date_i18n( 'Y' )
543
- );
544
- $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + 60;
545
- $filters_normalized[ 'utime' ][ 'type' ] = 'H';
546
- }
547
- else if ( !empty( $filters_normalized[ 'date' ][ 'hour' ] ) ) {
548
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
549
- $filters_normalized[ 'date' ][ 'hour' ],
550
- 0,
551
- 0,
552
- !empty( $filters_normalized[ 'date' ][ 'month' ] ) ? $filters_normalized[ 'date' ][ 'month' ] : date_i18n( 'n' ),
553
- !empty( $filters_normalized[ 'date' ][ 'day' ] ) ? $filters_normalized[ 'date' ][ 'day' ] : date_i18n( 'j' ),
554
- !empty( $filters_normalized[ 'date' ][ 'year' ] ) ? $filters_normalized[ 'date' ][ 'year' ] : date_i18n( 'Y' )
555
- );
556
- $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + 3599;
557
- $filters_normalized[ 'utime' ][ 'type' ] = 'H';
558
- }
559
- else if ( !empty( $filters_normalized[ 'date' ][ 'day' ] ) ) {
560
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
561
- 0,
562
- 0,
563
- 0,
564
- !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
565
- $filters_normalized[ 'date' ][ 'day' ],
566
- !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
567
- );
568
- $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + 86399;
569
- $filters_normalized[ 'utime' ][ 'type' ] = 'd';
570
- }
571
- else if( !empty( $filters_normalized[ 'date' ][ 'year' ] ) && empty( $filters_normalized[ 'date' ][ 'month' ] ) ) {
572
- $filters_normalized[ 'utime' ][ 'start' ] = mktime( 0, 0, 0, 1, 1, $filters_normalized[ 'date' ][ 'year' ] );
573
- $filters_normalized[ 'utime' ][ 'end' ] = mktime( 0, 0, 0, 1, 1, $filters_normalized[ 'date' ][ 'year' ] + 1 ) - 1;
574
- $filters_normalized[ 'utime' ][ 'type' ] = 'Y';
575
- }
576
- else if ( !empty( $filters_normalized[ 'date' ][ 'month' ] ) ) {
577
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
578
- 0,
579
- 0,
580
- 0,
581
- !empty( $filters_normalized[ 'date' ][ 'month' ] ) ? $filters_normalized[ 'date' ][ 'month' ] : date_i18n( 'n' ),
582
- 1,
583
- !empty( $filters_normalized[ 'date' ][ 'year' ] ) ? $filters_normalized[ 'date' ][ 'year' ] : date_i18n( 'Y' )
584
- );
585
-
586
- $filters_normalized[ 'utime' ][ 'end' ] = strtotime(
587
- ( !empty( $filters_normalized[ 'date' ][ 'year' ] ) ? $filters_normalized[ 'date' ][ 'year' ] : date_i18n( 'Y' ) ) . '-' .
588
- ( !empty( $filters_normalized[ 'date' ][ 'month' ] ) ? $filters_normalized[ 'date' ][ 'month' ] : date_i18n( 'n' ) ) .
589
- '-01 00:00 +1 month UTC'
590
- ) - 1;
591
- $filters_normalized[ 'utime' ][ 'type' ] = 'm';
592
- }
593
- else {
594
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
595
- 0,
596
- 0,
597
- 0,
598
- !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
599
- 1,
600
- !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
601
- );
602
-
603
- $filters_normalized[ 'utime' ][ 'end' ] = intval( strtotime(
604
- ( !empty( $filters_normalized[ 'date' ][ 'year' ] ) ? $filters_normalized[ 'date' ][ 'year' ] : date_i18n( 'Y' ) ) . '-' .
605
- ( !empty( $filters_normalized[ 'date' ][ 'month' ] ) ? $filters_normalized[ 'date' ][ 'month' ] : date_i18n( 'n' ) ) . '-01 00:00 +1 month UTC'
606
- ) - 1 );
607
- $filters_normalized[ 'utime' ][ 'type' ] = 'm';
608
- }
609
- }
610
- else { // An interval was specified
611
- $filters_normalized[ 'utime' ][ 'type' ] = 'interval';
612
-
613
- // Interval Direction: 1 = past, 2 = future
614
- $sign = ( !empty( $filters_normalized[ 'date' ][ 'interval_direction' ] ) && $filters_normalized[ 'date' ][ 'interval_direction' ] == 2 ) ? '+' : '-';
615
-
616
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
617
- !empty( $filters_normalized[ 'date' ][ 'hour' ] ) ? $filters_normalized[ 'date' ][ 'hour' ] : 0,
618
- !empty( $filters_normalized[ 'date' ][ 'minute' ] ) ? $filters_normalized[ 'date' ][ 'minute' ] : 0,
619
- 0,
620
- !empty( $filters_normalized[ 'date' ][ 'month' ] ) ? $filters_normalized[ 'date' ][ 'month' ] : date_i18n( 'n' ),
621
- !empty( $filters_normalized[ 'date' ][ 'day' ] ) ? $filters_normalized[ 'date' ][ 'day' ] : date_i18n( 'j' ),
622
- !empty( $filters_normalized[ 'date' ][ 'year' ] ) ? $filters_normalized[ 'date' ][ 'year' ] : date_i18n( 'Y' )
623
- );
624
-
625
- $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + intval( $sign . (
626
- ( !empty( $filters_normalized[ 'date' ][ 'interval' ] ) ? intval( $filters_normalized[ 'date' ][ 'interval' ] ) : 0 ) * 86400 +
627
- ( !empty( $filters_normalized[ 'date' ][ 'interval_hours' ] ) ? intval( $filters_normalized[ 'date' ][ 'interval_hours' ] ) : 0 ) * 3600 +
628
- ( !empty( $filters_normalized[ 'date' ][ 'interval_minutes' ] ) ? intval( $filters_normalized[ 'date' ][ 'interval_minutes' ] ) : 0 ) * 60
629
- ) );
630
-
631
- // Swap boundaries if we're going back in time
632
- if ( $filters_normalized[ 'date' ][ 'interval_direction' ] == 1 ) {
633
- $adjustment = ( abs( $filters_normalized[ 'utime' ][ 'start' ] - $filters_normalized[ 'utime' ][ 'end' ] ) < 86400 ) ? 0 : 86400;
634
- list( $filters_normalized[ 'utime' ][ 'start' ], $filters_normalized[ 'utime' ][ 'end' ] ) = array( $filters_normalized[ 'utime' ][ 'end' ] + $adjustment, $filters_normalized[ 'utime' ][ 'start' ] + $adjustment );
635
- }
636
-
637
- $filters_normalized[ 'utime' ][ 'end' ]--;
638
- }
639
-
640
- // If end is in the future, set it to now
641
- if ( $filters_normalized[ 'utime' ][ 'end' ] > date_i18n( 'U' ) ) {
642
- $filters_normalized[ 'utime' ][ 'end' ] = intval( date_i18n( 'U' ) );
643
- }
644
-
645
- // If start is after end, set it to first of month
646
- if ( $filters_normalized[ 'utime' ][ 'start' ] > $filters_normalized[ 'utime' ][ 'end' ] ) {
647
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
648
- 0,
649
- 0,
650
- 0,
651
- date_i18n( 'n', $filters_normalized[ 'utime' ][ 'end' ] ),
652
- 1,
653
- date_i18n( 'Y', $filters_normalized[ 'utime' ][ 'end' ] )
654
- );
655
- $filters_normalized[ 'date' ][ 'hour' ] = $filters_normalized[ 'date' ][ 'day' ] = $filters_normalized[ 'date' ][ 'month' ] = $filters_normalized[ 'date' ][ 'year' ] = 0;
656
- }
657
-
658
- // Restore filters on date_i18n
659
- wp_slimstat::toggle_date_i18n_filters( true );
660
-
661
- // Apply third-party filters
662
- $filters_normalized = apply_filters( 'slimstat_db_filters_normalized', $filters_normalized, $_filters_raw );
663
-
664
- return $filters_normalized;
665
- }
666
-
667
- // The following methods retrieve the information from the database
668
-
669
- public static function count_bouncing_pages() {
670
- $where = self::get_combined_where( 'visit_id > 0 AND content_type <> "404"', 'resource' );
671
-
672
- return intval( self::get_var( "
673
- SELECT COUNT(*) counthits
674
- FROM (
675
- SELECT resource, visit_id
676
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
677
- WHERE $where
678
- GROUP BY resource
679
- HAVING COUNT(visit_id) = 1
680
- ) as ts1",
681
- 'SUM(counthits) AS counthits' ) );
682
- }
683
-
684
- public static function count_exit_pages() {
685
- $where = self::get_combined_where( 'visit_id > 0', 'resource' );
686
-
687
- return intval( self::get_var( "
688
- SELECT COUNT(*) counthits
689
- FROM (
690
- SELECT resource, dt
691
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
692
- WHERE $where
693
- GROUP BY resource
694
- HAVING dt = MAX(dt)
695
- ) AS ts1",
696
- 'SUM(counthits) AS counthits' ) );
697
- }
698
-
699
- public static function count_records( $_column = 'id', $_where = '', $_use_date_filters = true ) {
700
- $distinct_column = ( $_column != 'id' ) ? "DISTINCT $_column" : $_column;
701
- $_where = self::get_combined_where( $_where, $_column, $_use_date_filters );
702
-
703
- return intval( self::get_var( "
704
- SELECT COUNT($distinct_column) counthits
705
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
706
- WHERE $_where",
707
- 'SUM(counthits) AS counthits' ) );
708
- }
709
-
710
- public static function count_records_having( $_column = 'id', $_where = '', $_having = '' ) {
711
- $_where = self::get_combined_where( $_where, $_column );
712
-
713
- return intval( self::get_var( "
714
- SELECT COUNT(*) counthits FROM (
715
- SELECT $_column
716
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
717
- WHERE $_where
718
- GROUP BY $_column
719
- HAVING $_having
720
- ) AS ts1",
721
- 'SUM(counthits) AS counthits' ) );
722
- }
723
-
724
- public static function get_data_for_chart( $_args = array() ) {
725
- $previous = array( 'end' => self::$filters_normalized[ 'utime' ][ 'start' ] - 1 );
726
- $label_date_format = '';
727
- $output = array( 'json' => '[]', 'json_count' => 0, 'current' => array( 'label' => '' ) );
728
-
729
- // Each type has its own parameters
730
- switch ( self::$filters_normalized[ 'utime' ][ 'type' ] ) {
731
- case 'H':
732
- $previous[ 'start' ] = self::$filters_normalized[ 'utime' ][ 'start' ] - 3600;
733
- $label_date_format = wp_slimstat::$settings[ 'time_format' ];
734
- $group_by = array( 'HOUR', 'MINUTE', 'i' );
735
- $values_in_interval = array( 59, 59, 0, 60 );
736
- break;
737
-
738
- case 'd':
739
- $previous[ 'start' ] = self::$filters_normalized[ 'utime' ][ 'start' ] - 86400;
740
- $label_date_format = ( self::$formats[ 'decimal' ] == '.' ) ? 'm/d' : 'd/m';
741
- $group_by = array( 'DAY', 'HOUR', 'G' );
742
- $values_in_interval = array( 23, 23, 0, 3600 );
743
- break;
744
-
745
- case 'Y':
746
- $previous[ 'start' ] = mktime( 0, 0, 0, 1, 1, self::$filters_normalized[ 'date' ][ 'year' ] - 1 );
747
- $label_date_format = 'Y';
748
- $group_by = array( 'YEAR', 'MONTH', 'n' );
749
- $values_in_interval = array( 12, 12, 1, 2678400 );
750
- break;
751
-
752
- case 'interval':
753
- $group_by = array( 'MONTH', 'DAY', 'j' );
754
- $default_interval = empty( self::$filters_normalized[ 'date' ][ 'interval' ] ) ? intval( wp_slimstat::$settings[ 'posts_column_day_interval' ] ) : self::$filters_normalized[ 'date' ][ 'interval' ];
755
- $values_in_interval = array( abs( $default_interval - 1 ), abs( $default_interval - 1 ), 0, 86400 );
756
- break;
757
-
758
- default:
759
- $previous[ 'start' ] = mktime( 0, 0, 0, ( !empty( self::$filters_normalized[ 'date' ][ 'month' ] ) ? self::$filters_normalized[ 'date' ][ 'month' ] : intval( date_i18n('n') ) ) - 1, 1, !empty( self::$filters_normalized[ 'date' ][ 'year' ]) ? self::$filters_normalized[ 'date' ][ 'year' ] : intval( date_i18n( 'Y' ) ) );
760
- $label_date_format = 'm/y';
761
- $group_by = array( 'MONTH', 'DAY', 'j' );
762
- $values_in_interval = array( intval( date( 't', $previous[ 'start' ] ) ), intval( date( 't', self::$filters_normalized[ 'utime' ][ 'start' ] ) ), 1, 86400 );
763
- break;
764
- }
765
-
766
- if ( empty( $_args[ 'where' ] ) ) {
767
- $_args[ 'where' ] = '';
768
- }
769
-
770
- // Custom intervals don't have a comparison chart ('previous' range)
771
- if ( self::$filters_normalized[ 'utime' ][ 'type' ] == 'interval' ) {
772
- $_args[ 'where' ] = self::get_combined_where( $_args[ 'where' ] );
773
- $previous_time_range = '';
774
- }
775
- else {
776
- $_args[ 'where' ] = self::get_combined_where( $_args[ 'where' ], '*', false );
777
- $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' ].')';
778
- }
779
-
780
- // Build the SQL query
781
- $group_by_string = "{$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'))";
782
- $sql = "
783
- SELECT dt, {$_args[ 'data1' ]} first_metric, {$_args[ 'data2' ]} second_metric
784
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
785
- WHERE {$_args[ 'where' ]} $previous_time_range
786
- GROUP BY $group_by_string";
787
-
788
- // Get the data
789
- $results = self::get_results(
790
- $sql,
791
- 'dt',
792
- '',
793
- $group_by_string, 'SUM(first_metric) AS first_metric, SUM(second_metric) AS second_metric' );
794
-
795
- // Fill the output array
796
- if ( self::$filters_normalized[ 'utime' ][ 'type' ] != 'interval' ) {
797
- $output[ 'current' ][ 'label' ] = gmdate( $label_date_format, self::$filters_normalized[ 'utime' ][ 'start' ] );
798
- $output[ 'previous' ][ 'label' ] = gmdate( $label_date_format, $previous[ 'start' ] );
799
-
800
- $output[ 'previous' ][ 'first_metric' ] = array_fill( $values_in_interval[ 2 ], $values_in_interval[ 0 ], 0 );
801
- $output[ 'previous' ][ 'second_metric' ] = array_fill( $values_in_interval[ 2 ], $values_in_interval[ 0 ], 0 );
802
- }
803
-
804
- $today_limit = floatval( date_i18n( 'Ymd.Hi' ) );
805
- for ( $i = $values_in_interval[ 2 ]; $i <= $values_in_interval[ 1 ]; $i++ ) {
806
- // Do not include dates in the future
807
- if ( floatval( date( 'Ymd.Hi', self::$filters_normalized[ 'utime' ][ 'start' ] + ( ( $i - $values_in_interval[ 2 ]) * $values_in_interval[ 3 ] ) ) ) <= $today_limit ) {
808
- $output[ 'current' ][ 'first_metric' ][ $i ] = 0;
809
- $output[ 'current' ][ 'second_metric' ][ $i ] = 0;
810
- }
811
- }
812
-
813
- // No data? No problem!
814
- if ( !is_array( $results ) || empty( $results ) ) {
815
- return $output;
816
- }
817
-
818
- // Rearrange the data
819
- foreach ( $results as $i => $a_result ) {
820
- $index = !empty( self::$filters_normalized[ 'date' ][ 'interval' ] ) ? floor( ( $a_result['dt'] - self::$filters_normalized[ 'utime' ][ 'start' ] ) / 86400 ) : gmdate( $group_by[ 2 ], $a_result[ 'dt' ] );
821
-
822
- if ( !empty( $previous[ 'start' ] ) && gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], $a_result[ 'dt' ] ) == gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], $previous[ 'start' ] ) ) {
823
- $output[ 'previous' ][ 'first_metric' ][ $index ] = intval( $a_result[ 'first_metric' ] );
824
- $output[ 'previous' ][ 'second_metric' ][ $index ] = intval( $a_result[ 'second_metric' ] );
825
- }
826
- if ( empty( $previous[ 'start' ] ) || gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], $a_result[ 'dt' ] ) == gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], self::$filters_normalized[ 'utime' ][ 'start' ] ) ) {
827
- $output[ 'current' ][ 'first_metric' ][ $index ] = intval( $a_result[ 'first_metric' ] );
828
- $output[ 'current' ][ 'second_metric' ][ $index ] = intval( $a_result[ 'second_metric' ] );
829
- }
830
- }
831
-
832
- // Now we're ready to repackage the data for the chart
833
- $js_chart_data = array();
834
- $output[ 'json_count' ] = max( $values_in_interval[ 0 ], $values_in_interval[ 1 ] );
835
- for ( $i = $values_in_interval[ 2 ]; $i <= $output[ 'json_count' ]; $i++ ) {
836
- $js_chart_data[ $i ][ 'date' ] = ( self::$filters_normalized[ 'utime' ][ 'type' ] == 'interval' ) ? date_i18n( 'm/d', self::$filters_normalized[ 'utime' ][ 'start' ] + $i * 86400 ) : $i;
837
-
838
- if ( isset( $output[ 'current' ][ 'first_metric' ][ $i ] ) ) {
839
- $js_chart_data[ $i ][ 'v1' ] = $output[ 'current' ][ 'first_metric' ][ $i ];
840
- $js_chart_data[ $i ][ 'v2' ] = $output[ 'current' ][ 'second_metric' ][ $i ];
841
- }
842
- if ( isset( $output[ 'previous' ][ 'first_metric' ][ $i ] ) ) {
843
- $js_chart_data[ $i ][ 'v3' ] = $output[ 'previous' ][ 'first_metric' ][ $i ];
844
- $js_chart_data[ $i ][ 'v4' ] = $output[ 'previous' ][ 'second_metric' ][ $i ];
845
- }
846
- }
847
-
848
- $output[ 'json' ] = json_encode( array_values( $js_chart_data ) );
849
-
850
- return $output;
851
- }
852
-
853
- public static function get_data_size() {
854
- $suffix = 'KB';
855
-
856
- $sql = 'SHOW TABLE STATUS LIKE "'.$GLOBALS[ 'wpdb' ]->prefix.'slim_stats"';
857
- $table_details = wp_slimstat::$wpdb->get_row( $sql, 'ARRAY_A', 0 );
858
-
859
- $table_size = ( $table_details[ 'Data_length' ] / 1024 ) + ( $table_details[ 'Index_length' ] / 1024 );
860
-
861
- if ( $table_size > 1024 ) {
862
- $table_size /= 1024;
863
- $suffix = 'MB';
864
- }
865
- return number_format( $table_size, 2, self::$formats[ 'decimal' ], self::$formats[ 'thousand' ] ).' '.$suffix;
866
- }
867
-
868
- public static function get_max_and_average_pages_per_visit() {
869
- $where = self::get_combined_where( 'visit_id > 0' );
870
-
871
- return self::get_results( "
872
- SELECT AVG(ts1.counthits) AS avghits, MAX(ts1.counthits) AS maxhits FROM (
873
- SELECT count(ip) counthits, visit_id
874
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
875
- WHERE $where
876
- GROUP BY visit_id
877
- ) AS ts1",
878
- 'blog_id',
879
- '',
880
- '',
881
- 'AVG(avghits) AS avghits, MAX(maxhits) AS maxhits' );
882
- }
883
-
884
- public static function get_oldest_visit() {
885
- return self::get_var( "
886
- SELECT dt
887
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
888
- ORDER BY dt ASC
889
- LIMIT 0, 1",
890
- 'MIN(dt)' );
891
- }
892
-
893
- public static function get_recent( $_column = 'id', $_where = '', $_having = '', $_use_date_filters = true, $_as_column = '', $_more_columns = '' ) {
894
- // This function can be passed individual arguments, or an array of arguments
895
- if ( is_array( $_column ) ) {
896
- $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
897
- $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
898
- $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
899
- $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
900
- $_more_columns = !empty( $_column[ 'more_columns' ] ) ? $_column[ 'more_columns' ] : '';
901
- $_column = $_column[ 'columns' ];
902
- }
903
-
904
- $columns = $_column;
905
- if ( !empty( $_as_column ) ) {
906
- $columns = "$_column AS $_as_column";
907
- }
908
-
909
- if ( $_column != '*' ) {
910
- $columns .= ', ip, dt';
911
- }
912
- else {
913
- $columns = 'id, ip, other_ip, username, country, city, location, referer, resource, searchterms, plugins, notes, visit_id, server_latency, page_performance, browser, browser_version, browser_type, platform, language, user_agent, resolution, screen_width, screen_height, content_type, category, author, content_id, outbound_resource, dt_out, dt';
914
- }
915
-
916
- if ( !empty( $_more_columns ) ) {
917
- $columns .= ', ' . $_more_columns;
918
- }
919
-
920
- $_where = self::get_combined_where( $_where, $_column, $_use_date_filters );
921
-
922
- $results = self::get_results( "
923
- SELECT $columns
924
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
925
- WHERE $_where
926
- ORDER BY dt DESC
927
- LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
928
- $columns,
929
- 'dt DESC' );
930
-
931
- if ( $_column != '*' ) {
932
- $column_values = array_map( 'unserialize', array_unique( array_map( 'serialize', self::array_column( $results, explode( ',', $_column ) ) ) ) );
933
- $results = array_intersect_key( $results, $column_values );
934
- }
935
-
936
- return $results;
937
- }
938
-
939
- public static function get_recent_events() {
940
- if ( empty( self::$filters_normalized[ 'columns' ] ) ) {
941
- $from = "{$GLOBALS['wpdb']->prefix}slim_events te";
942
- $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes' );
943
- }
944
- else {
945
- $from = "{$GLOBALS['wpdb']->prefix}slim_events te INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON te.id = t1.id";
946
- $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes', true, 't1' );
947
- }
948
-
949
- return self::get_results( "
950
- SELECT *
951
- FROM $from
952
- WHERE $where
953
- ORDER BY te.dt DESC"
954
- );
955
- }
956
-
957
- public static function get_recent_outbound() {
958
- $mixed_outbound_resources = self::get_recent( 'outbound_resource' );
959
- $clean_outbound_resources = array();
960
-
961
- foreach ( $mixed_outbound_resources as $a_mixed_resource ) {
962
- $exploded_resources = explode( ';;;', $a_mixed_resource[ 'outbound_resource' ] );
963
- foreach ( $exploded_resources as $a_exploded_resource ) {
964
- $a_mixed_resource[ 'outbound_resource' ] = $a_exploded_resource;
965
- $clean_outbound_resources[] = $a_mixed_resource;
966
- }
967
- }
968
-
969
- return $clean_outbound_resources;
970
- }
971
-
972
- public static function get_top( $_column = 'id', $_where = '', $_having = '', $_use_date_filters = true, $_as_column = '' ){
973
- // This function can be passed individual arguments, or an array of arguments
974
- if ( is_array( $_column ) ) {
975
- $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
976
- $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
977
- $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
978
- $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
979
- $_column = $_column[ 'columns' ];
980
- }
981
-
982
- if ( !empty( $_as_column ) ) {
983
- $_column = "$_column AS $_as_column";
984
- }
985
- else {
986
- $_as_column = $_column;
987
- }
988
-
989
- $_where = self::get_combined_where( $_where, $_as_column, $_use_date_filters );
990
-
991
- return self::get_results( "
992
- SELECT $_column, COUNT(*) counthits
993
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
994
- WHERE $_where
995
- GROUP BY $_as_column $_having
996
- ORDER BY counthits DESC
997
- LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
998
- ( ( !empty( $_as_column ) && $_as_column != $_column ) ? $_as_column : $_column ),
999
- 'counthits DESC',
1000
- ( ( !empty( $_as_column ) && $_as_column != $_column ) ? $_as_column : $_column ),
1001
- 'SUM(counthits) AS counthits' );
1002
- }
1003
-
1004
- public static function get_top_aggr( $_column = 'id', $_where = '', $_outer_select_column = '', $_aggr_function = 'MAX' ) {
1005
- if ( is_array( $_column ) ) {
1006
- $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
1007
- $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
1008
- $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
1009
- $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
1010
- $_outer_select_column = !empty( $_column[ 'outer_select_column' ] ) ? $_column[ 'outer_select_column' ] : '';
1011
- $_aggr_function = !empty( $_column[ 'aggr_function' ] ) ? $_column[ 'aggr_function' ] : '';
1012
- $_column = $_column[ 'columns' ];
1013
- }
1014
-
1015
- if ( !empty( $_as_column ) ) {
1016
- $_column = "$_column AS $_as_column";
1017
- }
1018
- else {
1019
- $_as_column = $_column;
1020
- }
1021
-
1022
- $_where = self::get_combined_where( $_where, $_column );
1023
-
1024
- return self::get_results( "
1025
- SELECT $_outer_select_column, ts1.aggrid as $_column, COUNT(*) counthits
1026
- FROM (
1027
- SELECT $_column, $_aggr_function(id) aggrid
1028
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
1029
- WHERE $_where
1030
- GROUP BY $_column
1031
- ) AS ts1 JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.aggrid = t1.id
1032
- GROUP BY $_outer_select_column
1033
- ORDER BY counthits DESC
1034
- LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
1035
- $_outer_select_column,
1036
- 'counthits DESC',
1037
- $_outer_select_column,
1038
- "$_aggr_function(aggrid), SUM(counthits)" );
1039
- }
1040
-
1041
- public static function get_top_events() {
1042
- if ( empty( self::$filters_normalized[ 'columns' ] ) ) {
1043
- $from = "{$GLOBALS['wpdb']->prefix}slim_events te";
1044
- $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes' );
1045
- }
1046
- else {
1047
- $from = "{$GLOBALS['wpdb']->prefix}slim_events te INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON te.id = t1.id";
1048
- $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes', true, 't1' );
1049
- }
1050
-
1051
- return self::get_results( "
1052
- SELECT te.notes, te.type, COUNT(*) counthits
1053
- FROM $from
1054
- WHERE $where
1055
- GROUP BY te.notes, te.type
1056
- ORDER BY counthits DESC"
1057
- );
1058
- }
1059
-
1060
- public static function get_top_outbound() {
1061
- $mixed_outbound_resources = self::get_recent( 'outbound_resource' );
1062
- $clean_outbound_resources = array();
1063
-
1064
- foreach ( $mixed_outbound_resources as $a_mixed_resource ) {
1065
- $exploded_resources = explode( ';;;', $a_mixed_resource[ 'outbound_resource' ] );
1066
- foreach ( $exploded_resources as $a_exploded_resource ) {
1067
- $clean_outbound_resources[] = $a_exploded_resource;
1068
- }
1069
- }
1070
-
1071
- $clean_outbound_resources = array_count_values( $clean_outbound_resources );
1072
- arsort( $clean_outbound_resources );
1073
-
1074
- $sorted_outbound_resources = array();
1075
- foreach ( $clean_outbound_resources as $a_resource => $a_count ) {
1076
- $sorted_outbound_resources[] = array(
1077
- 'outbound_resource' => $a_resource,
1078
- 'counthits' => $a_count
1079
- );
1080
- }
1081
-
1082
- return $sorted_outbound_resources;
1083
- }
1084
-
1085
- protected static function array_column( $input = array(), $columns = array() ) {
1086
- $output = array();
1087
-
1088
- foreach ( $input as $a_key => $a_row ) {
1089
- foreach ( $columns as $a_column ) {
1090
- $a_column = trim( $a_column );
1091
- if ( $a_row[ $a_column ] != NULL ) {
1092
- $output[ $a_key ][ $a_column ] = $a_row[ $a_column ];
1093
- }
1094
- }
1095
- }
1096
-
1097
- return $output;
1098
- }
1099
- }
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 $operator_names = array();
8
+ public static $filters_normalized = array();
9
+
10
+ // Number and date formats
11
+ public static $formats = array( 'decimal' => ',', 'thousand' => '.' );
12
+
13
+ // Structure that maps filters to SQL information (table names, clauses, lookup tables, etc)
14
+ public static $sql_where = array( 'columns' => '', 'time_range' => '' );
15
+
16
+ // Filters that are not visible in the dropdown
17
+ public static $all_columns_names = array();
18
+
19
+ // Debug message
20
+ public static $debug_message = '';
21
+
22
+ /*
23
+ * Sets the filters and other structures needed to store the data retrieved from the DB
24
+ */
25
+ public static function init( $_filters = '' ) {
26
+ // Decimal and thousand separators
27
+ if ( wp_slimstat::$settings[ 'use_european_separators' ] == 'no' ){
28
+ self::$formats[ 'decimal' ] = '.';
29
+ self::$formats[ 'thousand' ] = ',';
30
+ }
31
+
32
+ // List of supported filters and their friendly names
33
+ self::$columns_names = array(
34
+ 'browser' => array( __( 'Browser', 'wp-slimstat' ), 'varchar' ),
35
+ 'country' => array( __( 'Country Code', 'wp-slimstat' ), 'varchar' ),
36
+ 'ip' => array( __( 'IP Address', 'wp-slimstat' ), 'varchar' ),
37
+ 'searchterms' => array( __( 'Search Terms', 'wp-slimstat' ), 'varchar' ),
38
+ 'language' => array( __( 'Language Code', 'wp-slimstat' ), 'varchar' ),
39
+ 'platform' => array( __( 'Operating System', 'wp-slimstat' ), 'varchar' ),
40
+ 'resource' => array( __( 'Permalink', 'wp-slimstat' ), 'varchar' ),
41
+ 'referer' => array( __( 'Referer', 'wp-slimstat' ), 'varchar' ),
42
+ 'username' => array( __( 'Visitor\'s Username', 'wp-slimstat' ), 'varchar' ),
43
+ 'outbound_resource' => array( __( 'Outbound Link', 'wp-slimstat' ), 'varchar' ),
44
+ 'page_performance' => array( __( 'Page Speed', 'wp-slimstat' ), 'int' ),
45
+ 'no_filter_selected_2' => array( '', 'none' ),
46
+ 'no_filter_selected_3' => array( __( '-- Advanced filters --', 'wp-slimstat' ), 'none' ),
47
+ 'plugins' => array( __( 'Browser Capabilities', 'wp-slimstat' ), 'varchar' ),
48
+ 'browser_version' => array( __( 'Browser Version', 'wp-slimstat' ), 'varchar' ),
49
+ 'browser_type' => array( __( 'Browser Type', 'wp-slimstat' ), 'int' ),
50
+ 'user_agent' => array( __( 'User Agent', 'wp-slimstat' ), 'varchar' ),
51
+ 'city' => array( __( 'City', 'wp-slimstat' ), 'varchar' ),
52
+ 'location' => array( __( 'Coordinates', 'wp-slimstat' ), 'varchar' ),
53
+ 'notes' => array( __( 'Annotations', 'wp-slimstat' ), 'varchar' ),
54
+ 'server_latency' => array( __( 'Server Latency', 'wp-slimstat' ), 'int' ),
55
+ 'author' => array( __( 'Post Author', 'wp-slimstat' ), 'varchar' ),
56
+ 'category' => array( __( 'Post Category ID', 'wp-slimstat' ), 'varchar' ),
57
+ 'other_ip' => array( __( 'Originating IP', 'wp-slimstat' ), 'varchar' ),
58
+ 'content_type' => array( __( 'Resource Content Type', 'wp-slimstat' ), 'varchar' ),
59
+ 'content_id' => array( __( 'Resource ID', 'wp-slimstat' ), 'int' ),
60
+ 'screen_width' => array( __( 'Screen Width', 'wp-slimstat' ), 'int' ),
61
+ 'screen_height' => array( __( 'Screen Height', 'wp-slimstat' ), 'int' ),
62
+ 'resolution' => array( __( 'Viewport Size', 'wp-slimstat' ), 'varchar' ),
63
+ 'visit_id' => array( __( 'Visit ID', 'wp-slimstat' ), 'int' )
64
+ );
65
+
66
+ if ( wp_slimstat::$settings[ 'geolocation_country' ] == 'on' ) {
67
+ unset( self::$columns_names[ 'city' ] );
68
+ unset( self::$columns_names[ 'location' ] );
69
+ }
70
+
71
+ // List of supported filters and their friendly names
72
+ self::$operator_names = array(
73
+ 'equals' => __( 'equals', 'wp-slimstat' ),
74
+ 'is_not_equal_to' => __( 'is not equal to', 'wp-slimstat' ),
75
+ 'contains' => __( 'contains', 'wp-slimstat' ),
76
+ 'includes_in_set' => __( 'is included in', 'wp-slimstat' ),
77
+ 'does_not_contain' => __( 'does not contain', 'wp-slimstat' ),
78
+ 'starts_with' => __( 'starts with', 'wp-slimstat' ),
79
+ 'ends_with' => __( 'ends with', 'wp-slimstat' ),
80
+ 'sounds_like' => __( 'sounds like', 'wp-slimstat' ),
81
+ 'is_greater_than' => __( 'is greater than', 'wp-slimstat' ),
82
+ 'is_less_than' => __( 'is less than', 'wp-slimstat' ),
83
+ 'between' => __( 'is between (x,y)', 'wp-slimstat' ),
84
+ 'matches' => __( 'matches', 'wp-slimstat' ),
85
+ 'does_not_match' => __( 'does not match', 'wp-slimstat' ),
86
+ 'is_empty' => __( 'is empty', 'wp-slimstat' ),
87
+ 'is_not_empty' => __( 'is not empty', 'wp-slimstat' ),
88
+ );
89
+
90
+ // The following filters will not be displayed in the dropdown
91
+ self::$all_columns_names = array_merge( array(
92
+
93
+ // Date and Time
94
+ 'hour' => array( __( 'Hour', 'wp-slimstat' ), 'int' ),
95
+ 'day' => array( __( 'Day', 'wp-slimstat' ), 'int' ),
96
+ 'month' => array( __( 'Month', 'wp-slimstat' ), 'int' ),
97
+ 'year' => array( __( 'Year', 'wp-slimstat' ), 'int' ),
98
+ 'interval' => array( __( 'days', 'wp-slimstat' ), 'int' ),
99
+ 'interval_hours' => array( __( 'hours', 'wp-slimstat' ), 'int' ),
100
+ 'dt' => array( __( 'Timestamp', 'wp-slimstat' ), 'int' ),
101
+ 'dt_out' => array( __( 'Exit Timestamp', 'wp-slimstat' ), 'int' ),
102
+
103
+ // Other columns
104
+ 'language_calculated' => array( __( 'Language', 'wp-slimstat' ), 'varchar' ),
105
+ 'platform_calculated' => array( __( 'Operating System', 'wp-slimstat' ), 'varchar' ),
106
+ 'resource_calculated' => array( __( 'Permalink', 'wp-slimstat' ), 'varchar' ),
107
+ 'referer_calculated' => array( __( 'Referer', 'wp-slimstat' ), 'varchar' ),
108
+ 'metric' => array( __( 'Metric', 'wp-slimstat' ), 'varchar' ),
109
+ 'value' => array( __( 'Value', 'wp-slimstat' ), 'varchar' ),
110
+ 'counthits' => array( __( 'Hits', 'wp-slimstat' ), 'int' ),
111
+ 'percentage' => array( __( 'Percentage', 'wp-slimstat' ), 'int' ),
112
+ 'tooltip' => array( __( 'Notes', 'wp-slimstat' ), 'varchar' ),
113
+ 'details' => array( __( 'Notes', 'wp-slimstat' ), 'varchar' ),
114
+
115
+ // Events
116
+ 'event_id' => array( __( 'Event ID', 'wp-slimstat' ), 'int' ),
117
+ 'type' => array( __( 'Type', 'wp-slimstat' ), 'int' ),
118
+ 'event_description' => array( __( 'Event Description', 'wp-slimstat' ), 'varchar' ),
119
+ 'position' => array( __( 'Event Coordinates', 'wp-slimstat' ), 'int' ),
120
+
121
+ 'limit_results' => array( __( 'Max Results', 'wp-slimstat' ), 'int' ),
122
+ 'start_from' => array( __( 'Offset', 'wp-slimstat' ), 'int' ),
123
+
124
+ // Misc Filters
125
+ 'strtotime' => array( 0, 'int' )
126
+ ), self::$columns_names );
127
+
128
+ // Allow third party plugins to add even more column names to the array
129
+ self::$all_columns_names = apply_filters( 'slimstat_column_names', self::$all_columns_names );
130
+
131
+ // Filters use the following format: browser equals Firefox&&&country contains gb
132
+ $filters_array = array();
133
+
134
+ // Filters are set via javascript as hidden fields and submitted as a POST request. They override anything passed through the regular input fields
135
+ if ( !empty( $_POST[ 'fs' ] ) && is_array( $_POST[ 'fs' ] ) ) {
136
+ foreach( $_POST[ 'fs' ] as $a_request_filter_name => $a_request_filter_value ) {
137
+ $filters_array[ htmlspecialchars( $a_request_filter_name ) ] = "$a_request_filter_name $a_request_filter_value";
138
+ }
139
+ }
140
+
141
+ // Date filters (input fields)
142
+ foreach ( array( 'hour', 'day', 'month', 'year', 'interval', 'interval_hours' ) as $a_date_time_filter_name ) {
143
+ if ( isset( $_POST[ $a_date_time_filter_name ] ) && strlen( $_POST[ $a_date_time_filter_name ] ) > 0 ) { // here we use isset instead of !empty to handle ZERO as a valid input value
144
+ $filters_array[ $a_date_time_filter_name ] = "$a_date_time_filter_name equals " . intval( $_POST[ $a_date_time_filter_name ] );
145
+ }
146
+ }
147
+
148
+ // Fields and drop downs
149
+ if ( !empty( $_POST[ 'f' ] ) && !empty( $_POST[ 'o' ] ) ) {
150
+ $filters_array[ htmlspecialchars( $_POST[ 'f' ] ) ] = "{$_POST[ 'f' ]} {$_POST[ 'o' ]} " . ( isset( $_POST[ 'v' ] ) ? $_POST[ 'v' ] : '' );
151
+ }
152
+
153
+ // Filters set via the plugin options
154
+ if ( wp_slimstat::$settings[ 'restrict_authors_view' ] == 'on' && !current_user_can( 'manage_options' ) && !empty( $GLOBALS[ 'current_user' ]->user_login ) ) {
155
+ $filters_array[ 'author' ] = 'author equals ' . $GLOBALS[ 'current_user' ]->user_login;
156
+ }
157
+
158
+ if ( !empty( $filters_array ) ) {
159
+ $filters_raw = implode( '&&&', $filters_array );
160
+ }
161
+
162
+ // Filters are defined as: browser equals Chrome&&&country starts_with en
163
+ if ( !isset( $filters_raw ) || !is_string( $filters_raw ) ) {
164
+ $filters_raw = '';
165
+ }
166
+
167
+ if ( !empty( $_filters ) && is_string( $_filters ) ) {
168
+ if ( !empty( $filters_raw ) ) {
169
+ $filters_raw = empty( $filters_raw ) ? $_filters : $_filters . '&&&' . $filters_raw;
170
+ }
171
+ else {
172
+ $filters_raw = $_filters;
173
+ }
174
+ }
175
+
176
+ // Hook for the... filters
177
+ $filters_raw = apply_filters( 'slimstat_db_pre_filters', $filters_raw );
178
+
179
+ // Normalize the filters
180
+ self::$filters_normalized = self::init_filters( $filters_raw );
181
+ }
182
+ // end init
183
+
184
+ /**
185
+ * Builds the array of WHERE clauses to be used later in our SQL queries
186
+ */
187
+ protected static function _get_sql_where( $_filters_normalized = array(), $_slim_stats_table_alias = '' ) {
188
+ $sql_array = array();
189
+
190
+ foreach ( $_filters_normalized as $a_filter_column => $a_filter_data ) {
191
+ // Add-ons can set their own custom filters, which are ignored here
192
+ if ( strpos( $a_filter_column, 'addon_' ) !== false ) {
193
+ continue;
194
+ }
195
+
196
+ $sql_array[] = self::get_single_where_clause( $a_filter_column, $a_filter_data[ 0 ], $a_filter_data[ 1 ], $_slim_stats_table_alias );
197
+ }
198
+
199
+ // Flatten array
200
+ if ( !empty( $sql_array ) ) {
201
+ return implode( ' AND ', $sql_array );
202
+ }
203
+
204
+ return '';
205
+ }
206
+
207
+ public static function get_combined_where( $_where = '', $_column = '*', $_use_date_filters = true, $_slim_stats_table_alias = '' ) {
208
+ $dt_with_alias = 'dt';
209
+ if ( !empty( $_slim_stats_table_alias ) ) {
210
+ $dt_with_alias = $_slim_stats_table_alias . '.' . $dt_with_alias;
211
+ }
212
+
213
+ $time_range_condition = '';
214
+ if ( empty( $_where ) ) {
215
+ if ( !empty( self::$filters_normalized[ 'columns' ] ) ) {
216
+ $_where = self::_get_sql_where( self::$filters_normalized[ 'columns' ], $_slim_stats_table_alias );
217
+
218
+ if ($_use_date_filters) {
219
+ $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
220
+ }
221
+ }
222
+ elseif ( $_use_date_filters ) {
223
+ $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
224
+ }
225
+
226
+ // This could happen if we have custom filters (add-ons, third party tools)
227
+ if ( empty( $_where ) ) {
228
+ $_where = '1=1';
229
+ }
230
+ }
231
+ else {
232
+ if ( $_where != '1=1' && !empty( self::$filters_normalized[ 'columns' ] ) ) {
233
+ $new_clause = self::_get_sql_where( self::$filters_normalized[ 'columns' ], $_slim_stats_table_alias );
234
+
235
+ // This condition could be empty if it's related to a custom column
236
+ if ( !empty( $new_clause ) ) {
237
+ $_where .= ' AND ' . $new_clause;
238
+ }
239
+ }
240
+ if ( $_use_date_filters ) {
241
+ $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
242
+ }
243
+ }
244
+
245
+ if ( !empty( $_where ) && !empty( $time_range_condition ) ) {
246
+ $_where = "$_where AND $time_range_condition";
247
+ }
248
+ else {
249
+ $_where = trim( "$_where $time_range_condition" );
250
+ }
251
+
252
+ if ( !empty( $_column ) && !empty( self::$columns_names[ $_column ] ) ) {
253
+ $_column = str_replace( '_calculated', '', $_column );
254
+ $column_with_alias = $_column;
255
+ if ( !empty( $_slim_stats_table_alias ) ) {
256
+ $column_with_alias = $_slim_stats_table_alias . '.' . $column_with_alias;
257
+ }
258
+
259
+ $filter_empty = "$column_with_alias " . ( ( self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NULL' : '= 0' );
260
+ $filter_not_empty = "$column_with_alias " . ( ( self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NOT NULL' : '<> 0' );
261
+
262
+ if ( strpos( $_where, $filter_empty ) === false && strpos( $_where, $filter_not_empty) === false) {
263
+ $_where = "$filter_not_empty AND $_where";
264
+ }
265
+ }
266
+
267
+ return $_where;
268
+ }
269
+
270
+ /**
271
+ * Translates user-friendly operators into SQL conditions
272
+ */
273
+ public static function get_single_where_clause( $_column = 'id', $_operator = 'equals', $_value = '', $_slim_stats_table_alias = '' ) {
274
+ $filter_empty = ( !empty( self::$columns_names[ $_column ] ) && self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NULL' : '= 0';
275
+ $filter_not_empty = ( !empty( self::$columns_names[ $_column ] ) && self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NOT NULL' : '<> 0';
276
+
277
+ $_column = str_replace( '_calculated', '', $_column );
278
+
279
+ $column_with_alias = $_column;
280
+ if ( !empty( $_slim_stats_table_alias ) ) {
281
+ $column_with_alias = $_slim_stats_table_alias . '.' . $_column;
282
+ }
283
+
284
+ switch( $_column ) {
285
+ case 'ip':
286
+ case 'other_ip':
287
+ $filter_empty = '= "0.0.0.0"';
288
+ break;
289
+ default:
290
+ break;
291
+ }
292
+
293
+ $where = array( '', $_value );
294
+ switch ( $_operator ) {
295
+ case 'is_not_equal_to':
296
+ $where[ 0 ] = "$column_with_alias <> %s";
297
+ break;
298
+
299
+ case 'contains':
300
+ $where = array( "$column_with_alias LIKE %s", '%'.$_value.'%' );
301
+ break;
302
+
303
+ case 'includes_in_set':
304
+ case 'included_in_set':
305
+ $where[ 0 ] = "FIND_IN_SET($column_with_alias, %s) > 0";
306
+ break;
307
+
308
+ case 'does_not_contain':
309
+ $where = array( "$column_with_alias NOT LIKE %s", '%'.$_value.'%' );
310
+ break;
311
+
312
+ case 'starts_with':
313
+ $where = array( "$column_with_alias LIKE %s", $_value.'%' );
314
+ break;
315
+
316
+ case 'ends_with':
317
+ $where = array( "$column_with_alias LIKE %s", '%'.$_value );
318
+ break;
319
+
320
+ case 'sounds_like':
321
+ $where[ 0 ] = "SOUNDEX($column_with_alias) = SOUNDEX(%s)";
322
+ break;
323
+
324
+ case 'is_empty':
325
+ $where = array( "$column_with_alias $filter_empty", '' );
326
+ break;
327
+
328
+ case 'is_not_empty':
329
+ $where = array( "$column_with_alias $filter_not_empty", '' );
330
+ break;
331
+
332
+ case 'is_greater_than':
333
+ $where[ 0 ] = "$column_with_alias > %d";
334
+ break;
335
+
336
+ case 'is_less_than':
337
+ $where[ 0 ] = "$column_with_alias < %d";
338
+ break;
339
+
340
+ case 'between':
341
+ $range = explode( ',', $_value );
342
+ $where = array( "$column_with_alias BETWEEN %d AND %d", array( $range[ 0 ], $range[ 1 ] ) );
343
+ break;
344
+
345
+ case 'matches':
346
+ $where[ 0 ] = "$column_with_alias REGEXP %s";
347
+ break;
348
+
349
+ case 'does_not_match':
350
+ $where[ 0 ] = "$column_with_alias NOT REGEXP %s";
351
+ break;
352
+
353
+ default:
354
+ $where[ 0 ] = "$column_with_alias = %s";
355
+ break;
356
+ }
357
+
358
+ if ( isset( $where[ 1 ] ) && $where[ 1 ] != '' ) {
359
+ return $GLOBALS[ 'wpdb' ]->prepare( $where[ 0 ], $where[ 1 ] );
360
+ }
361
+ else {
362
+ return $where[ 0 ];
363
+ }
364
+ }
365
+
366
+ public static function get_results( $_sql = '', $_select_no_aggregate_values = '', $_order_by = '', $_group_by = '', $_aggregate_values_add = '' ) {
367
+ $_sql = apply_filters( 'slimstat_get_results_sql', $_sql, $_select_no_aggregate_values, $_order_by, $_group_by, $_aggregate_values_add );
368
+
369
+ if ( wp_slimstat::$settings[ 'show_sql_debug' ] == 'on' ) {
370
+ self::$debug_message .= "<p class='debug'>$_sql</p>";
371
+ }
372
+
373
+ return wp_slimstat::$wpdb->get_results( $_sql, ARRAY_A );
374
+ }
375
+
376
+ public static function get_var( $_sql = '', $_aggregate_value = '' ) {
377
+ $_sql = apply_filters( 'slimstat_get_var_sql', $_sql, $_aggregate_value );
378
+
379
+ if ( wp_slimstat::$settings[ 'show_sql_debug' ] == 'on' ) {
380
+ self::$debug_message .= "<p class='debug'>$_sql</p>";
381
+ }
382
+
383
+ return wp_slimstat::$wpdb->get_var( $_sql );
384
+ }
385
+
386
+ public static function parse_filters( $_filters_raw ) {
387
+ $filters_parsed = array(
388
+ 'columns' => array(),
389
+ 'date' => array()
390
+ );
391
+
392
+ if ( !empty( $_filters_raw ) ) {
393
+ $matches = explode( '&&&', $_filters_raw );
394
+
395
+ foreach( $matches as $idx => $a_match ) {
396
+ preg_match( '/([^\s]+)\s([^\s]+)\s(.+)?/', urldecode( $a_match ), $a_filter );
397
+
398
+ 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 ) ) {
399
+ continue;
400
+ }
401
+
402
+ switch( $a_filter[ 1 ] ) {
403
+ case 'strtotime':
404
+ $custom_date = strtotime( $a_filter[ 3 ], date_i18n( 'U' ) );
405
+
406
+ $filters_parsed[ 'date' ][ 'hour' ] = intval( date( 'H', $custom_date ) );
407
+ $filters_parsed[ 'date' ][ 'day' ] = intval( date( 'j', $custom_date ) );
408
+ $filters_parsed[ 'date' ][ 'month' ] = intval( date( 'n', $custom_date ) );
409
+ $filters_parsed[ 'date' ][ 'year' ] = intval( date( 'Y', $custom_date ) );
410
+ break;
411
+
412
+ case 'hour':
413
+ case 'day':
414
+ case 'month':
415
+ case 'year':
416
+ if ( is_numeric( $a_filter[ 3 ] ) ) {
417
+ $filters_parsed[ 'date' ][ $a_filter[ 1 ] ] = intval( $a_filter[ 3 ] );
418
+ }
419
+ else{
420
+ // Try to apply strtotime to value
421
+ switch( $a_filter[ 1 ] ) {
422
+ case 'hour':
423
+ $filters_parsed[ 'date' ][ 'hour' ] = intval( date( 'H', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
424
+ break;
425
+
426
+ case 'day':
427
+ $filters_parsed[ 'date' ][ 'day' ] = intval( date( 'j', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
428
+ break;
429
+
430
+ case 'month':
431
+ $filters_parsed[ 'date' ][ 'month' ] = intval( date( 'n', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
432
+ break;
433
+
434
+ case 'year':
435
+ $filters_parsed[ 'date' ][ 'year' ] = intval( date( 'Y', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
436
+ break;
437
+
438
+ default:
439
+ break;
440
+ }
441
+
442
+ if ( $filters_parsed[ 'date' ][ $a_filter[ 1 ] ] === false ) {
443
+ unset( $filters_parsed[ 'date' ][ $a_filter[ 1 ] ] );
444
+ }
445
+ }
446
+ break;
447
+
448
+ case 'interval':
449
+ case 'interval_hours':
450
+ $intval_filter = intval( $a_filter[ 3 ] );
451
+ $filters_parsed[ 'date' ][ $a_filter[ 1 ] ] = $intval_filter;
452
+ break;
453
+
454
+ case 'limit_results':
455
+ case 'start_from':
456
+ $filters_parsed[ 'misc' ][ $a_filter[ 1 ] ] = str_replace( '\\', '', htmlspecialchars_decode( $a_filter[ 3 ] ) );
457
+ break;
458
+
459
+ case 'content_id':
460
+ if ( !empty( $a_filter[ 3 ] ) ) {
461
+ $content_id = ( $a_filter[ 3 ] == 'current' && !empty( $GLOBALS[ 'post' ]->ID ) ) ? $GLOBALS[ 'post' ]->ID : $a_filter[ 3 ];
462
+ $filters_parsed[ 'columns' ][ $a_filter[ 1 ] ] = array( $a_filter[ 2 ], $content_id );
463
+ break;
464
+ }
465
+ // no break here: if value IS numeric, go to the default parser here below
466
+
467
+ default:
468
+ $filters_parsed[ 'columns' ][ $a_filter[ 1 ] ] = array( $a_filter[ 2 ], isset( $a_filter[ 3 ] ) ? str_replace( '\\', '', htmlspecialchars_decode( $a_filter[ 3 ] ) ) : '' );
469
+ break;
470
+ }
471
+ }
472
+ }
473
+
474
+ return $filters_parsed;
475
+ }
476
+
477
+ public static function init_filters( $_filters_raw = '' ) {
478
+ $fn = self::parse_filters( $_filters_raw );
479
+
480
+ // Initialize default values
481
+ if ( empty( $fn[ 'misc' ][ 'limit_results' ] ) ) {
482
+ $fn[ 'misc' ][ 'limit_results' ] = wp_slimstat::$settings[ 'limit_results' ];
483
+ }
484
+ if ( empty( $fn[ 'misc' ][ 'start_from' ] ) ) {
485
+ $fn[ 'misc' ][ 'start_from' ] = 0;
486
+ }
487
+
488
+ $fn[ 'utime' ] = array(
489
+ 'start' => 0,
490
+ 'end' => 0
491
+ );
492
+
493
+ // Temporarily disable any filters on date_i18n
494
+ wp_slimstat::toggle_date_i18n_filters( false );
495
+
496
+ // Normalize the various date values
497
+
498
+ // Intervals
499
+ // If neither an interval nor interval_hours were specified...
500
+ if ( !isset( $fn[ 'date' ][ 'interval_hours' ] ) && empty( $fn[ 'date' ][ 'interval' ] ) ) {
501
+ $fn[ 'date' ][ 'interval_hours' ] = 0;
502
+
503
+ // If a day has been specified, then interval = 1 (show only that day)
504
+ if ( !empty( $fn[ 'date' ][ 'day' ] ) ) {
505
+ $fn[ 'date' ][ 'interval' ] = -1;
506
+ }
507
+ // Show last X days, if the corresponding setting is enabled
508
+ else if ( empty( wp_slimstat::$settings[ 'use_current_month_timespan' ] ) || wp_slimstat::$settings[ 'use_current_month_timespan' ] != 'on' ) {
509
+ $fn[ 'date' ][ 'interval' ] = - abs( wp_slimstat::$settings[ 'posts_column_day_interval' ] );
510
+ }
511
+ // Otherwise, the interval is the number of days from the beginning of the month (current month view)
512
+ else {
513
+ $fn[ 'date' ][ 'interval' ] = - intval( date_i18n( 'j' ) );
514
+ }
515
+ }
516
+ else if ( empty( $fn[ 'date' ][ 'interval_hours' ] ) ) {
517
+ // interval was set, but not interval_hours
518
+ $fn[ 'date' ][ 'interval_hours' ] = 0;
519
+ }
520
+ else if ( empty( $fn[ 'date' ][ 'interval' ] ) ) {
521
+ // interval_hours was set, but not interval
522
+ $fn[ 'date' ][ 'interval' ] = 0;
523
+ }
524
+
525
+ $fn[ 'utime' ][ 'range' ] = $fn[ 'date' ][ 'interval' ] * 86400 + $fn[ 'date' ][ 'interval_hours' ] * 3600;
526
+
527
+ // Day
528
+ if ( empty( $fn[ 'date' ][ 'day' ] ) ) {
529
+ $fn[ 'date' ][ 'day' ] = intval( date_i18n( 'j' ) );
530
+ }
531
+
532
+ // Month
533
+ if ( empty( $fn[ 'date' ][ 'month' ] ) ) {
534
+ $fn[ 'date' ][ 'month' ] = intval( date_i18n( 'n' ) );
535
+ }
536
+
537
+ // Year
538
+ if ( empty( $fn[ 'date' ][ 'year' ] ) ) {
539
+ $fn[ 'date' ][ 'year' ] = intval( date_i18n( 'Y' ) );
540
+ }
541
+
542
+ if ( $fn[ 'utime' ][ 'range' ] < 0 ) {
543
+ $fn[ 'utime' ][ 'end' ] = mktime(
544
+ !empty( $fn[ 'date' ][ 'hour' ] ) ? $fn[ 'date' ][ 'hour' ] : 23,
545
+ 59,
546
+ 59,
547
+ $fn[ 'date' ][ 'month' ],
548
+ $fn[ 'date' ][ 'day' ],
549
+ $fn[ 'date' ][ 'year' ]
550
+ );
551
+
552
+ // If end is in the future and the level of granularity is hours, set it to now
553
+ if ( !empty( $fn[ 'date' ][ 'interval_hours' ] ) && $fn[ 'utime' ][ 'end' ] > date_i18n( 'U' ) ) {
554
+ $fn[ 'utime' ][ 'end' ] = intval( date_i18n( 'U' ) );
555
+ }
556
+
557
+ $fn[ 'utime' ][ 'range' ] = $fn[ 'utime' ][ 'range' ] + 1;
558
+ $fn[ 'utime' ][ 'start' ] = $fn[ 'utime' ][ 'end' ] + $fn[ 'utime' ][ 'range' ];
559
+
560
+ // Store the absolute value for later (chart)
561
+ $fn[ 'utime' ][ 'range' ] = - $fn[ 'utime' ][ 'range' ];
562
+ }
563
+ else {
564
+ $fn[ 'utime' ][ 'start' ] = mktime(
565
+ !empty( $fn[ 'date' ][ 'hour' ] ) ? $fn[ 'date' ][ 'hour' ] : 0,
566
+ 0,
567
+ 0,
568
+ $fn[ 'date' ][ 'month' ],
569
+ $fn[ 'date' ][ 'day' ],
570
+ $fn[ 'date' ][ 'year' ]
571
+ );
572
+
573
+ $fn[ 'utime' ][ 'range' ] = $fn[ 'utime' ][ 'range' ] - 1;
574
+ $fn[ 'utime' ][ 'end' ] = $fn[ 'utime' ][ 'start' ] + $fn[ 'utime' ][ 'range' ];
575
+ }
576
+
577
+ // If end is in the future, set it to now
578
+ if ( $fn[ 'utime' ][ 'end' ] > date_i18n( 'U' ) ) {
579
+ $fn[ 'utime' ][ 'end' ] = intval( date_i18n( 'U' ) );
580
+ }
581
+
582
+ // Restore filters on date_i18n
583
+ wp_slimstat::toggle_date_i18n_filters( true );
584
+
585
+ // Apply third-party filters
586
+ $fn = apply_filters( 'slimstat_db_filters_normalized', $fn, $_filters_raw );
587
+
588
+ return $fn;
589
+ }
590
+
591
+ // The following methods retrieve the information from the database
592
+
593
+ public static function count_bouncing_pages() {
594
+ $where = self::get_combined_where( 'visit_id > 0 AND content_type <> "404"', 'resource' );
595
+
596
+ return intval( self::get_var( "
597
+ SELECT COUNT(*) counthits
598
+ FROM (
599
+ SELECT resource, visit_id
600
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
601
+ WHERE $where
602
+ GROUP BY resource
603
+ HAVING COUNT(visit_id) = 1
604
+ ) as ts1",
605
+ 'SUM(counthits) AS counthits' ) );
606
+ }
607
+
608
+ public static function count_exit_pages() {
609
+ $where = self::get_combined_where( 'visit_id > 0', 'resource' );
610
+
611
+ return intval( self::get_var( "
612
+ SELECT COUNT(*) counthits
613
+ FROM (
614
+ SELECT resource, dt
615
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
616
+ WHERE $where
617
+ GROUP BY resource
618
+ HAVING dt = MAX(dt)
619
+ ) AS ts1",
620
+ 'SUM(counthits) AS counthits' ) );
621
+ }
622
+
623
+ public static function count_records( $_column = 'id', $_where = '', $_use_date_filters = true ) {
624
+ $distinct_column = ( $_column != 'id' ) ? "DISTINCT $_column" : $_column;
625
+ $_where = self::get_combined_where( $_where, $_column, $_use_date_filters );
626
+
627
+ return intval( self::get_var( "
628
+ SELECT COUNT($distinct_column) counthits
629
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
630
+ WHERE $_where",
631
+ 'SUM(counthits) AS counthits' ) );
632
+ }
633
+
634
+ public static function count_records_having( $_column = 'id', $_where = '', $_having = '' ) {
635
+ $_where = self::get_combined_where( $_where, $_column );
636
+
637
+ return intval( self::get_var( "
638
+ SELECT COUNT(*) counthits FROM (
639
+ SELECT $_column
640
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
641
+ WHERE $_where
642
+ GROUP BY $_column
643
+ HAVING $_having
644
+ ) AS ts1",
645
+ 'SUM(counthits) AS counthits' ) );
646
+ }
647
+
648
+ public static function get_data_for_chart( $_args = array() ) {
649
+ // Determine the chart granularity based on the date range
650
+ // - Up to 24 hours (86400 seconds): HOURLY
651
+ // - Up to 120 days (10368000 seconds): DAILY
652
+ // - Otherwise: MONTHLY
653
+ $params = array();
654
+
655
+ if ( self::$filters_normalized[ 'utime' ][ 'range' ] < 86400 ) {
656
+ $params[ 'group_by' ] = "DAY(CONVERT_TZ(FROM_UNIXTIME(dt), @@session.time_zone, '+00:00')), HOUR(CONVERT_TZ(FROM_UNIXTIME(dt), @@session.time_zone, '+00:00'))";
657
+ $params[ 'data_points_label' ] = ( self::$formats[ 'decimal' ] == '.' ) ? 'm/d - h a' : 'd/m - H';
658
+ $params[ 'data_points_count' ] = ceil( self::$filters_normalized[ 'utime' ][ 'range' ] / 3600 );
659
+ $params[ 'granularity' ] = 'HOUR';
660
+ }
661
+ else if ( self::$filters_normalized[ 'utime' ][ 'range' ] < 10368000 ) {
662
+ $params[ 'group_by' ] = "MONTH(CONVERT_TZ(FROM_UNIXTIME(dt), @@session.time_zone, '+00:00')), DAY(CONVERT_TZ(FROM_UNIXTIME(dt), @@session.time_zone, '+00:00'))";
663
+ $params[ 'data_points_label' ] = ( self::$formats[ 'decimal' ] == '.' ) ? 'm/d' : 'd/m';
664
+ $params[ 'data_points_count' ] = ceil( self::$filters_normalized[ 'utime' ][ 'range' ] / 86400 );
665
+ $params[ 'granularity' ] = 'DAY';
666
+ }
667
+ else {
668
+ $params[ 'group_by' ] = "YEAR(CONVERT_TZ(FROM_UNIXTIME(dt), @@session.time_zone, '+00:00')), MONTH(CONVERT_TZ(FROM_UNIXTIME(dt), @@session.time_zone, '+00:00'))";
669
+ $params[ 'data_points_label' ] = 'm/y';
670
+ $params[ 'data_points_count' ] = self::count_months_between( self::$filters_normalized[ 'utime' ][ 'start' ], self::$filters_normalized[ 'utime' ][ 'end' ] );
671
+ $params[ 'granularity' ] = 'MONTH';
672
+ }
673
+
674
+ // Calculate the "previous/comparison" time range
675
+ $params[ 'previous_end' ] = self::$filters_normalized[ 'utime' ][ 'start' ] - 1;
676
+ $params[ 'previous_start' ] = $params[ 'previous_end' ] - self::$filters_normalized[ 'utime' ][ 'range' ];
677
+
678
+ // Build the SQL query
679
+ if ( empty( $_args[ 'where' ] ) ) {
680
+ $_args[ 'where' ] = '';
681
+ }
682
+
683
+ $sql = "
684
+ SELECT MIN(dt) AS dt, {$_args[ 'data1' ]} AS v1, {$_args[ 'data2' ]} AS v2
685
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
686
+ WHERE " . self::get_combined_where( $_args[ 'where' ], '*', false ) . " AND (dt BETWEEN {$params[ 'previous_start' ]} AND {$params[ 'previous_end' ]} OR dt BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ] . ")
687
+ GROUP BY {$params[ 'group_by' ]}";
688
+
689
+ // Get the data
690
+ $results = self::get_results(
691
+ $sql,
692
+ 'dt',
693
+ '',
694
+ $params[ 'group_by' ], 'SUM(v1) AS v1, SUM(v2) AS v2'
695
+ );
696
+
697
+ $output = array(
698
+ 'keys' => array()
699
+ );
700
+
701
+ // No data? No problem!
702
+ if ( !is_array( $results ) || empty( $results ) ) {
703
+ return $output;
704
+ }
705
+
706
+ // Generate the output array (sent to the chart library) by combining all the data collected so far
707
+
708
+ // Let's start by initializing all the data points to zero
709
+ for ( $i = 0; $i < $params[ 'data_points_count' ]; $i++ ) {
710
+ $v1_label = date( $params[ 'data_points_label' ], strtotime( "+$i {$params[ 'granularity' ]}", self::$filters_normalized[ 'utime' ][ 'start' ] ) );
711
+ $v3_label = date( $params[ 'data_points_label' ], strtotime( "+$i {$params[ 'granularity' ]}", $params[ 'previous_start' ] ) );
712
+
713
+ $output[ 'keys' ][ $v1_label ] = $i;
714
+ $output[ 'keys' ][ $v3_label ] = $i;
715
+
716
+ // This is how AmCharts expects the data to be formatted
717
+ $output[ $i ][ 'v1_label' ] = $v1_label;
718
+ $output[ $i ][ 'v3_label' ] = $v3_label;
719
+ $output[ $i ][ 'v4' ] = $output[ $i ][ 'v3' ] = $output[ $i ][ 'v2' ] = $output[ $i ][ 'v1' ] = 0;
720
+ }
721
+
722
+
723
+
724
+ // Now populate all the data points
725
+ foreach ( $results as $a_result ) {
726
+ $label = date( $params[ 'data_points_label' ], $a_result[ 'dt' ] );
727
+
728
+ // Data out of range?
729
+ if ( !isset( $output[ 'keys' ][ $label ] ) ) {
730
+ continue;
731
+ }
732
+
733
+ // Does this value belong to the "current" range?
734
+ if ( $a_result[ 'dt' ] >= self::$filters_normalized[ 'utime' ][ 'start' ] && $a_result[ 'dt' ] <= self::$filters_normalized[ 'utime' ][ 'end' ] ) {
735
+ $output[ $output[ 'keys' ][ $label ] ][ 'v1' ] = intval( $a_result[ 'v1' ] );
736
+ $output[ $output[ 'keys' ][ $label ] ][ 'v2' ] = intval( $a_result[ 'v2' ] );
737
+ }
738
+ else {
739
+ $output[ $output[ 'keys' ][ $label ] ][ 'v3' ] = intval( $a_result[ 'v1' ] );
740
+ $output[ $output[ 'keys' ][ $label ] ][ 'v4' ] = intval( $a_result[ 'v2' ] );
741
+ }
742
+ }
743
+
744
+ return $output;
745
+ }
746
+
747
+ public static function get_data_size() {
748
+ $suffix = 'KB';
749
+
750
+ $sql = 'SHOW TABLE STATUS LIKE "'.$GLOBALS[ 'wpdb' ]->prefix.'slim_stats"';
751
+ $table_details = wp_slimstat::$wpdb->get_row( $sql, 'ARRAY_A', 0 );
752
+
753
+ $table_size = ( $table_details[ 'Data_length' ] / 1024 ) + ( $table_details[ 'Index_length' ] / 1024 );
754
+
755
+ if ( $table_size > 1024 ) {
756
+ $table_size /= 1024;
757
+ $suffix = 'MB';
758
+ }
759
+ return number_format( $table_size, 2, self::$formats[ 'decimal' ], self::$formats[ 'thousand' ] ).' '.$suffix;
760
+ }
761
+
762
+ public static function get_max_and_average_pages_per_visit() {
763
+ $where = self::get_combined_where( 'visit_id > 0' );
764
+
765
+ return self::get_results( "
766
+ SELECT AVG(ts1.counthits) AS avghits, MAX(ts1.counthits) AS maxhits FROM (
767
+ SELECT count(ip) counthits, visit_id
768
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
769
+ WHERE $where
770
+ GROUP BY visit_id
771
+ ) AS ts1",
772
+ 'blog_id',
773
+ '',
774
+ '',
775
+ 'AVG(avghits) AS avghits, MAX(maxhits) AS maxhits' );
776
+ }
777
+
778
+ public static function get_oldest_visit() {
779
+ return self::get_var( "
780
+ SELECT dt
781
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
782
+ ORDER BY dt ASC
783
+ LIMIT 0, 1",
784
+ 'MIN(dt)' );
785
+ }
786
+
787
+ public static function get_recent( $_column = 'id', $_where = '', $_having = '', $_use_date_filters = true, $_as_column = '', $_more_columns = '' ) {
788
+ // This function can be passed individual arguments, or an array of arguments
789
+ if ( is_array( $_column ) ) {
790
+ $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
791
+ $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
792
+ $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
793
+ $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
794
+ $_more_columns = !empty( $_column[ 'more_columns' ] ) ? $_column[ 'more_columns' ] : '';
795
+ $_column = $_column[ 'columns' ];
796
+ }
797
+
798
+ $columns = $_column;
799
+ if ( !empty( $_as_column ) ) {
800
+ $columns = "$_column AS $_as_column";
801
+ }
802
+
803
+ if ( $_column != '*' ) {
804
+ $columns .= ', ip, dt';
805
+ }
806
+ else {
807
+ $columns = 'id, ip, other_ip, username, country, city, location, referer, resource, searchterms, plugins, notes, visit_id, server_latency, page_performance, browser, browser_version, browser_type, platform, language, user_agent, resolution, screen_width, screen_height, content_type, category, author, content_id, outbound_resource, dt_out, dt';
808
+ }
809
+
810
+ if ( !empty( $_more_columns ) ) {
811
+ $columns .= ', ' . $_more_columns;
812
+ }
813
+
814
+ $_where = self::get_combined_where( $_where, $_column, $_use_date_filters );
815
+
816
+ $results = self::get_results( "
817
+ SELECT $columns
818
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
819
+ WHERE $_where
820
+ ORDER BY dt DESC
821
+ LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
822
+ $columns,
823
+ 'dt DESC' );
824
+
825
+ if ( $_column != '*' ) {
826
+ $column_values = array_map( 'unserialize', array_unique( array_map( 'serialize', self::array_column( $results, explode( ',', $_column ) ) ) ) );
827
+ $results = array_intersect_key( $results, $column_values );
828
+ }
829
+
830
+ return $results;
831
+ }
832
+
833
+ public static function get_recent_events() {
834
+ if ( empty( self::$filters_normalized[ 'columns' ] ) ) {
835
+ $from = "{$GLOBALS['wpdb']->prefix}slim_events te";
836
+ $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes' );
837
+ }
838
+ else {
839
+ $from = "{$GLOBALS['wpdb']->prefix}slim_events te INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON te.id = t1.id";
840
+ $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes', true, 't1' );
841
+ }
842
+
843
+ return self::get_results( "
844
+ SELECT *
845
+ FROM $from
846
+ WHERE $where
847
+ ORDER BY te.dt DESC"
848
+ );
849
+ }
850
+
851
+ public static function get_recent_outbound() {
852
+ $mixed_outbound_resources = self::get_recent( 'outbound_resource' );
853
+ $clean_outbound_resources = array();
854
+
855
+ foreach ( $mixed_outbound_resources as $a_mixed_resource ) {
856
+ $exploded_resources = explode( ';;;', $a_mixed_resource[ 'outbound_resource' ] );
857
+ foreach ( $exploded_resources as $a_exploded_resource ) {
858
+ $a_mixed_resource[ 'outbound_resource' ] = $a_exploded_resource;
859
+ $clean_outbound_resources[] = $a_mixed_resource;
860
+ }
861
+ }
862
+
863
+ return $clean_outbound_resources;
864
+ }
865
+
866
+ public static function get_top( $_column = 'id', $_where = '', $_having = '', $_use_date_filters = true, $_as_column = '' ){
867
+ // This function can be passed individual arguments, or an array of arguments
868
+ if ( is_array( $_column ) ) {
869
+ $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
870
+ $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
871
+ $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
872
+ $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
873
+ $_column = $_column[ 'columns' ];
874
+ }
875
+
876
+ if ( !empty( $_as_column ) ) {
877
+ $_column = "$_column AS $_as_column";
878
+ }
879
+ else {
880
+ $_as_column = $_column;
881
+ }
882
+
883
+ $_where = self::get_combined_where( $_where, $_as_column, $_use_date_filters );
884
+
885
+ return self::get_results( "
886
+ SELECT $_column, COUNT(*) counthits
887
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
888
+ WHERE $_where
889
+ GROUP BY $_as_column $_having
890
+ ORDER BY counthits DESC
891
+ LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
892
+ ( ( !empty( $_as_column ) && $_as_column != $_column ) ? $_as_column : $_column ),
893
+ 'counthits DESC',
894
+ ( ( !empty( $_as_column ) && $_as_column != $_column ) ? $_as_column : $_column ),
895
+ 'SUM(counthits) AS counthits' );
896
+ }
897
+
898
+ public static function get_top_aggr( $_column = 'id', $_where = '', $_outer_select_column = '', $_aggr_function = 'MAX' ) {
899
+ if ( is_array( $_column ) ) {
900
+ $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
901
+ $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
902
+ $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
903
+ $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
904
+ $_outer_select_column = !empty( $_column[ 'outer_select_column' ] ) ? $_column[ 'outer_select_column' ] : '';
905
+ $_aggr_function = !empty( $_column[ 'aggr_function' ] ) ? $_column[ 'aggr_function' ] : '';
906
+ $_column = $_column[ 'columns' ];
907
+ }
908
+
909
+ if ( !empty( $_as_column ) ) {
910
+ $_column = "$_column AS $_as_column";
911
+ }
912
+ else {
913
+ $_as_column = $_column;
914
+ }
915
+
916
+ $_where = self::get_combined_where( $_where, $_column );
917
+
918
+ return self::get_results( "
919
+ SELECT $_outer_select_column, ts1.aggrid as $_column, COUNT(*) counthits
920
+ FROM (
921
+ SELECT $_column, $_aggr_function(id) aggrid
922
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
923
+ WHERE $_where
924
+ GROUP BY $_column
925
+ ) AS ts1 JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.aggrid = t1.id
926
+ GROUP BY $_outer_select_column
927
+ ORDER BY counthits DESC
928
+ LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
929
+ $_outer_select_column,
930
+ 'counthits DESC',
931
+ $_outer_select_column,
932
+ "$_aggr_function(aggrid), SUM(counthits)" );
933
+ }
934
+
935
+ public static function get_top_events() {
936
+ if ( empty( self::$filters_normalized[ 'columns' ] ) ) {
937
+ $from = "{$GLOBALS['wpdb']->prefix}slim_events te";
938
+ $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes' );
939
+ }
940
+ else {
941
+ $from = "{$GLOBALS['wpdb']->prefix}slim_events te INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON te.id = t1.id";
942
+ $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes', true, 't1' );
943
+ }
944
+
945
+ return self::get_results( "
946
+ SELECT te.notes, te.type, COUNT(*) counthits
947
+ FROM $from
948
+ WHERE $where
949
+ GROUP BY te.notes, te.type
950
+ ORDER BY counthits DESC"
951
+ );
952
+ }
953
+
954
+ public static function get_top_outbound() {
955
+ $mixed_outbound_resources = self::get_recent( 'outbound_resource' );
956
+ $clean_outbound_resources = array();
957
+
958
+ foreach ( $mixed_outbound_resources as $a_mixed_resource ) {
959
+ $exploded_resources = explode( ';;;', $a_mixed_resource[ 'outbound_resource' ] );
960
+ foreach ( $exploded_resources as $a_exploded_resource ) {
961
+ $clean_outbound_resources[] = $a_exploded_resource;
962
+ }
963
+ }
964
+
965
+ $clean_outbound_resources = array_count_values( $clean_outbound_resources );
966
+ arsort( $clean_outbound_resources );
967
+
968
+ $sorted_outbound_resources = array();
969
+ foreach ( $clean_outbound_resources as $a_resource => $a_count ) {
970
+ $sorted_outbound_resources[] = array(
971
+ 'outbound_resource' => $a_resource,
972
+ 'counthits' => $a_count
973
+ );
974
+ }
975
+
976
+ return $sorted_outbound_resources;
977
+ }
978
+
979
+ protected static function array_column( $input = array(), $columns = array() ) {
980
+ $output = array();
981
+
982
+ foreach ( $input as $a_key => $a_row ) {
983
+ foreach ( $columns as $a_column ) {
984
+ $a_column = trim( $a_column );
985
+ if ( $a_row[ $a_column ] != NULL ) {
986
+ $output[ $a_key ][ $a_column ] = $a_row[ $a_column ];
987
+ }
988
+ }
989
+ }
990
+
991
+ return $output;
992
+ }
993
+
994
+ protected static function count_months_between( $min_timestamp = 0, $max_timestamp = 0 ) {
995
+ $i = 0;
996
+ $min_month = date( 'Ym', $min_timestamp );
997
+ $max_month = date( 'Ym', $max_timestamp );
998
+
999
+ while ( $min_month <= $max_month ) {
1000
+ $min_timestamp = strtotime( "+1 month", $min_timestamp );
1001
+ $min_month = date( 'Ym', $min_timestamp );
1002
+ $i++;
1003
+ }
1004
+
1005
+ return $i;
1006
+ }
1007
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/view/wp-slimstat-reports.php CHANGED
@@ -1,2197 +1,2192 @@
1
- <?php
2
-
3
- class wp_slimstat_reports {
4
-
5
- // Structures to store all the information about what screens and reports are available
6
- public static $reports_info = array();
7
- public static $user_reports = array();
8
-
9
- // Useful data for the reports
10
- protected static $pageviews = 0;
11
-
12
- /**
13
- * Initalize class properties
14
- */
15
- public static function init(){
16
-
17
- // Include and initialize the API to interact with the database
18
- include_once( 'wp-slimstat-db.php' );
19
- wp_slimstat_db::init();
20
-
21
- // Retrieve data that will be used by multiple reports
22
- self::$pageviews = wp_slimstat_db::count_records();
23
-
24
- // Define all the reports
25
- //
26
- // Parameters
27
- // - title : report name
28
- // - callback : function to use to render the report
29
- // - callback_args : parameters to pass to the function
30
- // - classes : determine the look and feel of this report ( tall, large, extralarge, full-width, hidden )
31
- // - screens : where should the report appear ( slimview1, .., slimview4, dashboard )
32
- // - tooltip : contextual help to be displayed on hover
33
-
34
- $chart_tooltip = '<strong>' . __( 'Chart controls', 'wp-slimstat' ) . '</strong><ul><li>' . __( 'Use your mouse wheel to zoom in and out', 'wp-slimstat' ) . '</li><li>' . __( 'While zooming in, drag the chart to move to a different area', 'wp-slimstat' ) . '</li></ul>';
35
-
36
- self::$reports_info = array(
37
- 'slim_p7_02' => array(
38
- 'title' => __( 'Visitors Activity', 'wp-slimstat' ),
39
- 'callback' => array( __CLASS__, 'show_activity_log' ),
40
- 'callback_args' => array(
41
- 'type' => 'recent',
42
- 'columns' => '*',
43
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
44
- ),
45
- 'classes' => array( 'full-width', 'tall' ),
46
- 'screens' => array( 'slimview1', 'dashboard' ),
47
- 'tooltip' => __( 'Color codes', 'wp-slimstat' ).'</strong><p><span class="little-color-box is-search-engine"></span> '.__( 'From search result page', 'wp-slimstat' ).'</p><p><span class="little-color-box is-known-visitor"></span> '.__( 'Known Visitor', 'wp-slimstat' ).'</p><p><span class="little-color-box is-known-user"></span> '.__( 'Known Users', 'wp-slimstat' ).'</p><p><span class="little-color-box is-direct"></span> '.__( 'Other Humans', 'wp-slimstat' ).'</p><p><span class="little-color-box"></span> '.__( 'Bot or Crawler', 'wp-slimstat' ).'</p>'
48
- ),
49
-
50
- 'slim_p1_01' => array(
51
- 'title' => __( 'Pageviews', 'wp-slimstat' ),
52
- 'callback' => array( __CLASS__, 'show_chart' ),
53
- 'callback_args' => array(
54
- 'id' => 'slim_p1_01',
55
- 'chart_data' => array(
56
- 'data1' => 'COUNT( ip )',
57
- 'data2' => 'COUNT( DISTINCT ip )'
58
- ),
59
- 'chart_labels' => array(
60
- __( 'Pageviews', 'wp-slimstat' ),
61
- __( 'Unique IPs', 'wp-slimstat' )
62
- )
63
- ),
64
- 'classes' => array( 'extralarge', 'chart' ),
65
- 'screens' => array( 'slimview2', 'dashboard' ),
66
- 'tooltip' => $chart_tooltip
67
- ),
68
- 'slim_p1_02' => array(
69
- 'title' => __( 'About Slimstat', 'wp-slimstat' ),
70
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
71
- 'callback_args' => array(
72
- 'raw' => array( __CLASS__, 'get_about_wpslimstat' )
73
- ),
74
- 'classes' => array( 'normal', 'hidden' ),
75
- 'screens' => array( 'slimview2' )
76
- ),
77
- 'slim_p1_03' => array(
78
- 'title' => __( 'Traffic at a Glance', 'wp-slimstat' ),
79
- // 'callback' => array( __CLASS__, 'show_overview_summary' ),
80
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
81
- 'callback_args' => array(
82
- 'raw' => array( __CLASS__, 'get_overview_summary' )
83
- ),
84
- 'classes' => array( 'normal' ),
85
- 'screens' => array( 'slimview2', 'dashboard' )
86
- ),
87
- 'slim_p1_04' => array(
88
- 'title' => __( 'Currently Online', 'wp-slimstat' ),
89
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
90
- 'callback_args' => array(
91
- 'type' => 'recent',
92
- 'columns' => 'ip',
93
- 'where' => 'dt_out > '. ( date_i18n( 'U' ) - 300 ) . ' OR dt > '. ( date_i18n( 'U' ) - 300 ),
94
- 'use_date_filters' => false,
95
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
96
- ),
97
- 'classes' => array( 'normal' ),
98
- 'screens' => array( 'slimview2', 'dashboard' )
99
- ),
100
- 'slim_p1_06' => array(
101
- 'title' => __( 'Recent Search Terms', 'wp-slimstat' ),
102
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
103
- 'callback_args' => array(
104
- 'type' => 'recent',
105
- 'columns' => 'searchterms',
106
- 'where' => 'searchterms <> "_" AND searchterms <> "" AND searchterms IS NOT NULL',
107
- 'more_columns' => 'referer, resource',
108
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
109
- ),
110
- 'classes' => array( 'normal' ),
111
- 'screens' => array( 'slimview2', 'slimview5' ),
112
- 'tooltip' => __( 'Keywords used by your visitors to find your website on a search engine.', 'wp-slimstat' )
113
- ),
114
- 'slim_p1_08' => array(
115
- 'title' => __( 'Top Web Pages', 'wp-slimstat' ),
116
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
117
- 'callback_args' => array(
118
- 'type' => 'top',
119
- 'columns' => 'SUBSTRING_INDEX(resource, "' . ( !get_option( 'permalink_structure' ) ? '&' : '?' ) . '", 1)',
120
- 'as_column' => 'resource_calculated',
121
- 'filter_op' => 'contains',
122
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
123
- ),
124
- 'classes' => array( 'normal' ),
125
- 'screens' => array( 'slimview2', 'dashboard' ),
126
- 'tooltip' => __( 'Here a "page" is not just a WordPress page type, but any webpage on your site, including posts, products, categories, and so on. You can set the corresponding filter where Resource Content Type equals cpt:you_cpt_slug_here to get top web pages for a specific custom post type you have.', 'wp-slimstat' )
127
- ),
128
- 'slim_p1_10' => array(
129
- 'title' => __('Top Referring Domains', 'wp-slimstat'),
130
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
131
- 'callback_args' => array(
132
- 'type' => 'top',
133
- 'columns' => 'REPLACE( SUBSTRING_INDEX( ( SUBSTRING_INDEX( ( SUBSTRING_INDEX( referer, "://", -1 ) ), "/", 1 ) ), ".", -5 ), "www.", "" )',
134
- 'as_column' => 'referer_calculated',
135
- 'filter_op' => 'contains',
136
- 'where' => 'referer NOT LIKE "%' . str_replace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) ) . '%"',
137
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
138
- ),
139
- 'classes' => array( 'normal' ),
140
- 'screens' => array( 'slimview2', 'slimview5', 'dashboard' )
141
- ),
142
- 'slim_p1_11' => array(
143
- 'title' => __( 'Top Known Visitors', 'wp-slimstat' ),
144
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
145
- 'callback_args' => array(
146
- 'type' => 'top',
147
- 'columns' => 'username',
148
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
149
- ),
150
- 'classes' => array( 'normal', 'hidden' ),
151
- 'screens' => array( 'slimview2', 'dashboard' )
152
- ),
153
- 'slim_p1_12' => array(
154
- 'title' => __( 'Top Search Terms', 'wp-slimstat' ),
155
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
156
- 'callback_args' => array(
157
- 'type' => 'top',
158
- 'columns' => 'searchterms',
159
- 'where' => 'searchterms <> "_" AND searchterms <> "" AND searchterms IS NOT NULL',
160
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
161
- ),
162
- 'classes' => array( 'normal' ),
163
- 'screens' => array( 'slimview2', 'slimview4', 'slimview5', 'dashboard' )
164
- ),
165
- 'slim_p1_13' => array(
166
- 'title' => __( 'Top Countries', 'wp-slimstat' ),
167
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
168
- 'callback_args' => array(
169
- 'type' => 'top',
170
- 'columns' => 'country',
171
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
172
- ),
173
- 'classes' => array( 'normal', 'hidden' ),
174
- 'screens' => array( 'slimview2', 'slimview3', 'slimview5', 'dashboard' ),
175
- 'tooltip' => __( 'You can configure Slimstat to ignore a specific Country by setting the corresponding filter under Settings > Slimstat > Filters.', 'wp-slimstat' )
176
- ),
177
- 'slim_p1_15' => array(
178
- 'title' => __( 'Rankings', 'wp-slimstat' ),
179
- 'callback' => array( __CLASS__, 'show_rankings' ),
180
- 'callback_args' => array(
181
- 'id' => 'slim_p1_15'
182
- ),
183
- 'classes' => array( 'normal', 'hidden' ),
184
- 'screens' => array( 'slimview2' ),
185
- 'tooltip' => __( "Slimstat retrieves live information from Alexa, Facebook and Mozscape, to measures your site's rankings. Values are updated every 12 hours. Please enter your personal access ID in the settings to access your personalized Mozscape data.", 'wp-slimstat' )
186
- ),
187
- 'slim_p1_17' => array(
188
- 'title' => __( 'Top Language Families', 'wp-slimstat' ),
189
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
190
- 'callback_args' => array(
191
- 'type' => 'top',
192
- 'columns' => 'SUBSTRING(language, 1, 2)',
193
- 'as_column' => 'language_calculated',
194
- 'filter_op' => 'contains',
195
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
196
- ),
197
- 'classes' => array( 'normal', 'hidden' ),
198
- 'screens' => array( 'slimview3' )
199
- ),
200
- 'slim_p1_18' => array(
201
- 'title' => __( 'Users Currently Online', 'wp-slimstat' ),
202
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
203
- 'callback_args' => array(
204
- 'type' => 'recent',
205
- 'columns' => 'username',
206
- 'where' => 'dt_out > '. ( date_i18n( 'U' ) - 300 ) . ' OR dt > '. ( date_i18n( 'U' ) - 300 ),
207
- 'use_date_filters' => false,
208
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
209
- ),
210
- 'classes' => array( 'normal', 'hidden' ),
211
- 'screens' => array( 'slimview2', 'dashboard' ),
212
- 'tooltip' => __( 'When visitors leave a comment on your blog, WordPress assigns them a cookie. Slimstat leverages this information to identify returning visitors. Please note that visitors also include registered users.', 'wp-slimstat' )
213
- ),
214
- 'slim_p1_19_01' => array( // Chart Reports need to always have a _01 suffix to tell our custom "refresh" code to avoid fading the chart, which apparently doesn't work
215
- 'title' => __( 'Search Terms', 'wp-slimstat' ),
216
- 'callback' => array( __CLASS__, 'show_chart' ),
217
- 'callback_args' => array(
218
- 'id' => 'slim_p1_19_01',
219
- 'chart_data' => array(
220
- 'data1' => 'COUNT( searchterms )',
221
- 'data2' => 'COUNT( DISTINCT searchterms )',
222
- 'where' => 'searchterms <> "_" AND searchterms IS NOT NULL AND searchterms <> ""'
223
- ),
224
- 'chart_labels' => array(
225
- __( 'Search Terms', 'wp-slimstat' ),
226
- __( 'Unique Terms', 'wp-slimstat' )
227
- )
228
- ),
229
- 'classes' => array( 'extralarge', 'chart' ),
230
- 'screens' => array( 'slimview2' ),
231
- 'tooltip' => $chart_tooltip
232
- ),
233
- 'slim_p1_20' => array(
234
- 'title' => __('Top Referring URLs', 'wp-slimstat'),
235
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
236
- 'callback_args' => array(
237
- 'type' => 'top',
238
- 'columns' => 'referer',
239
- 'where' => 'referer NOT LIKE "%' . str_replace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) ) . '%"',
240
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
241
- ),
242
- 'classes' => array( 'normal' ),
243
- 'screens' => array( 'slimview2', 'slimview5', 'dashboard' )
244
- ),
245
-
246
- 'slim_p2_01' => array(
247
- 'title' => __( 'Human Visits', 'wp-slimstat' ),
248
- 'callback' => array( __CLASS__, 'show_chart' ),
249
- 'callback_args' => array(
250
- 'id' => 'slim_p2_01',
251
- 'chart_data' => array(
252
- 'data1' => 'COUNT( DISTINCT visit_id )',
253
- 'data2' => 'COUNT( DISTINCT ip )',
254
- 'where' => '(visit_id > 0 AND browser_type <> 1)'
255
- ),
256
- 'chart_labels' => array(
257
- __( 'Visits', 'wp-slimstat' ),
258
- __( 'Unique IPs', 'wp-slimstat' )
259
- )
260
- ),
261
- 'classes' => array( 'extralarge', 'chart' ),
262
- 'screens' => array( 'slimview3' ),
263
- 'tooltip' => $chart_tooltip
264
- ),
265
- 'slim_p2_02' => array(
266
- 'title' => __( 'Audience Overview', 'wp-slimstat' ),
267
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
268
- 'callback_args' => array(
269
- 'raw' => array( __CLASS__, 'get_visitors_summary' )
270
- ),
271
- 'classes' => array( 'normal' ),
272
- 'screens' => array( 'slimview3', 'dashboard' ),
273
- 'tooltip' => __( 'Where not otherwise specified, the metrics in this report are referred to human visitors.', 'wp-slimstat' )
274
- ),
275
- 'slim_p2_03' => array(
276
- 'title' => __( 'Top Languages', 'wp-slimstat' ),
277
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
278
- 'callback_args' => array(
279
- 'type' => 'top',
280
- 'columns' => 'language',
281
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
282
- ),
283
- 'classes' => array( 'normal' ),
284
- 'screens' => array( 'slimview3' )
285
- ),
286
- 'slim_p2_04' => array(
287
- 'title' => __( 'Top Browsers', 'wp-slimstat' ),
288
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
289
- 'callback_args' => array(
290
- 'type' => 'top',
291
- 'columns' => 'browser, browser_version',
292
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
293
- ),
294
- 'classes' => array( 'normal' ),
295
- 'screens' => array( 'slimview3', 'dashboard' )
296
- ),
297
- 'slim_p2_05' => array(
298
- 'title' => __( 'Top Service Providers', 'wp-slimstat' ),
299
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
300
- 'callback_args' => array(
301
- 'type' => 'top',
302
- 'columns' => 'ip',
303
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
304
- ),
305
- 'classes' => array( 'extralarge', 'hidden' ),
306
- 'screens' => array( 'slimview3' ),
307
- 'tooltip' => __( 'Internet Service Provider: a company which provides other companies or individuals with access to the Internet. Your DSL or cable internet service is provided to you by your ISP.<br><br>You can ignore specific IP addresses by setting the corresponding filter under Settings > Slimstat > Filters.', 'wp-slimstat' )
308
- ),
309
- 'slim_p2_06' => array(
310
- 'title' => __( 'Top Operating Systems', 'wp-slimstat' ),
311
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
312
- 'callback_args' => array(
313
- 'type' => 'top',
314
- 'columns' => 'platform',
315
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
316
- ),
317
- 'classes' => array( 'normal', 'hidden' ),
318
- 'screens' => array( 'slimview3' ),
319
- 'tooltip' => __( 'Internet Service Provider: a company which provides other companies or individuals with access to the Internet. Your DSL or cable internet service is provided to you by your ISP.<br><br>You can ignore specific IP addresses by setting the corresponding filter under Settings > Slimstat > Filters.', 'wp-slimstat' )
320
- ),
321
- 'slim_p2_07' => array(
322
- 'title' => __( 'Top Screen Resolutions', 'wp-slimstat' ),
323
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
324
- 'callback_args' => array(
325
- 'type' => 'top',
326
- 'columns' => 'screen_width, screen_height',
327
- 'where' => 'screen_width <> 0 AND screen_height <> 0',
328
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
329
- ),
330
- 'classes' => array( 'normal' ),
331
- 'screens' => array( 'slimview3', 'dashboard' )
332
- ),
333
- 'slim_p2_08' => array(
334
- 'title' => __( 'Top Viewport Sizes', 'wp-slimstat' ),
335
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
336
- 'callback_args' => array(
337
- 'type' => 'top',
338
- 'columns' => 'resolution',
339
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
340
- ),
341
- 'classes' => array( 'normal', 'hidden' ),
342
- 'screens' => array( 'slimview3' )
343
- ),
344
- 'slim_p2_09' => array(
345
- 'title' => __( 'Browser Capabilities', 'wp-slimstat' ),
346
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
347
- 'callback_args' => array(
348
- 'raw' => array( __CLASS__, 'get_plugins' )
349
- ),
350
- 'classes' => array( 'normal', 'hidden' ),
351
- 'screens' => array( 'slimview3' )
352
- ),
353
- 'slim_p2_12' => array(
354
- 'title' => __( 'Visit Duration', 'wp-slimstat' ),
355
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
356
- 'callback_args' => array(
357
- 'raw' => array( __CLASS__, 'get_visits_duration' )
358
- ),
359
- 'classes' => array( 'normal', 'hidden' ),
360
- 'screens' => array( 'slimview3' ),
361
- 'tooltip' => __( 'All values represent the percentages of pageviews within the corresponding time range.', 'wp-slimstat' )
362
- ),
363
- 'slim_p2_13' => array(
364
- 'title' => __( 'Recent Countries', 'wp-slimstat' ),
365
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
366
- 'callback_args' => array(
367
- 'type' => 'recent',
368
- 'columns' => 'country',
369
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
370
- ),
371
- 'classes' => array( 'normal', 'hidden' ),
372
- 'screens' => array( 'slimview3', 'slimview5' )
373
- ),
374
- 'slim_p2_14' => array(
375
- 'title' => __( 'Recent Viewport Sizes', 'wp-slimstat' ),
376
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
377
- 'callback_args' => array(
378
- 'type' => 'recent',
379
- 'columns' => 'resolution',
380
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
381
- ),
382
- 'classes' => array( 'normal', 'hidden' ),
383
- 'screens' => array( 'slimview3' )
384
- ),
385
- 'slim_p2_15' => array(
386
- 'title' => __( 'Recent Operating Systems', 'wp-slimstat' ),
387
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
388
- 'callback_args' => array(
389
- 'type' => 'recent',
390
- 'columns' => 'platform',
391
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
392
- ),
393
- 'classes' => array( 'normal', 'hidden' ),
394
- 'screens' => array( 'slimview3' )
395
- ),
396
- 'slim_p2_16' => array(
397
- 'title' => __( 'Recent Browsers', 'wp-slimstat' ),
398
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
399
- 'callback_args' => array(
400
- 'type' => 'recent',
401
- 'columns' => 'browser, browser_version',
402
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
403
- ),
404
- 'classes' => array( 'normal', 'hidden' ),
405
- 'screens' => array( 'slimview3' )
406
- ),
407
- 'slim_p2_17' => array(
408
- 'title' => __( 'Recent Languages', 'wp-slimstat' ),
409
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
410
- 'callback_args' => array(
411
- 'type' => 'recent',
412
- 'columns' => 'language',
413
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
414
- ),
415
- 'classes' => array( 'normal', 'hidden' ),
416
- 'screens' => array( 'slimview3' )
417
- ),
418
- 'slim_p2_18' => array(
419
- 'title' => __( 'Top Browser Families', 'wp-slimstat' ),
420
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
421
- 'callback_args' => array(
422
- 'type' => 'top',
423
- 'columns' => 'browser',
424
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
425
- ),
426
- 'classes' => array( 'normal', 'hidden' ),
427
- 'screens' => array( 'slimview3' ),
428
- 'tooltip' => __( 'This report shows you what user agent families (no version considered) are popular among your visitors.', 'wp-slimstat' )
429
- ),
430
- 'slim_p2_19' => array(
431
- 'title' => __( 'Top OS Families', 'wp-slimstat' ),
432
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
433
- 'callback_args' => array(
434
- 'type' => 'top',
435
- 'columns' => 'CONCAT("p-", SUBSTRING(platform, 1, 3))',
436
- 'as_column' => 'platform_calculated',
437
- 'filter_op' => 'contains',
438
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
439
- ),
440
- 'classes' => array( 'normal' ),
441
- 'screens' => array( 'slimview3' ),
442
- 'tooltip' => __( 'This report shows you what operating system families (no version considered) are popular among your visitors.', 'wp-slimstat' )
443
- ),
444
- 'slim_p2_20' => array(
445
- 'title' => __( 'Recent Users', 'wp-slimstat' ),
446
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
447
- 'callback_args' => array(
448
- 'type' => 'recent',
449
- 'columns' => 'username',
450
- 'where' => 'notes LIKE "%user:%"',
451
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
452
- ),
453
- 'classes' => array( 'normal' ),
454
- 'screens' => array( 'slimview3' )
455
- ),
456
- 'slim_p2_21' => array(
457
- 'title' => __( 'Top Users', 'wp-slimstat' ),
458
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
459
- 'callback_args' => array(
460
- 'type' => 'top',
461
- 'columns' => 'username',
462
- 'where' => 'notes LIKE "%user:%"',
463
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
464
- ),
465
- 'classes' => array( 'normal', 'hidden' ),
466
- 'screens' => array( 'slimview3', 'dashboard' )
467
- ),
468
- 'slim_p2_22_01' => array( // Chart Reports need to always have a _01 suffix to tell our custom "refresh" code to avoid fading the chart, which apparently doesn't work
469
- 'title' => __( 'Users', 'wp-slimstat' ),
470
- 'callback' => array( __CLASS__, 'show_chart' ),
471
- 'callback_args' => array(
472
- 'id' => 'slim_p2_22_01',
473
- 'chart_data' => array(
474
- 'data1' => 'COUNT( username )',
475
- 'data2' => 'COUNT( DISTINCT username )'
476
- ),
477
- 'chart_labels' => array(
478
- __( 'Users', 'wp-slimstat' ),
479
- __( 'Unique Users', 'wp-slimstat' )
480
- )
481
- ),
482
- 'classes' => array( 'extralarge', 'chart' ),
483
- 'screens' => array( 'slimview3' ),
484
- 'tooltip' => $chart_tooltip
485
- ),
486
-
487
- 'slim_p3_01' => array(
488
- 'title' => __( 'Traffic Sources', 'wp-slimstat' ),
489
- 'callback' => array( __CLASS__, 'show_chart' ),
490
- 'callback_args' => array(
491
- 'id' => 'slim_p3_01',
492
- 'chart_data' => array(
493
- 'data1' => 'COUNT( DISTINCT referer )',
494
- 'data2' => 'COUNT( DISTINCT ip )',
495
- 'where' => '(referer IS NOT NULL AND referer NOT LIKE "%' . home_url() . '%")'
496
- ),
497
- 'chart_labels' => array(
498
- __( 'Domains', 'wp-slimstat' ),
499
- __( 'Unique IPs', 'wp-slimstat' )
500
- )
501
- ),
502
- 'classes' => array( 'extralarge', 'chart' ),
503
- 'screens' => array( 'slimview5' ),
504
- 'tooltip' => $chart_tooltip
505
- ),
506
- 'slim_p3_02' => array(
507
- 'title' => __( 'Traffic Summary', 'wp-slimstat' ),
508
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
509
- 'callback_args' => array(
510
- 'raw' => array( __CLASS__, 'get_traffic_sources_summary' )
511
- ),
512
- 'classes' => array( 'normal' ),
513
- 'screens' => array( 'slimview5' )
514
- ),
515
- 'slim_p3_06' => array(
516
- 'title' => __( 'Top Referring Search Engines', 'wp-slimstat' ),
517
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
518
- 'callback_args' => array(
519
- 'type' => 'top',
520
- 'columns' => 'REPLACE( SUBSTRING_INDEX( SUBSTRING_INDEX( SUBSTRING_INDEX( referer, "://", -1 ), "/", 1 ), ".", -5 ), "www.", "" )',
521
- 'as_column' => 'referer_calculated',
522
- 'filter_op' => 'contains',
523
- 'where' => 'searchterms IS NOT NULL AND searchterms <> "" AND searchterms <> "_" AND referer NOT LIKE "%' . str_replace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) ) . '%"',
524
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
525
- ),
526
- 'classes' => array( 'normal' ),
527
- 'screens' => array( 'slimview5', 'dashboard' )
528
- ),
529
-
530
- 'slim_p4_01' => array(
531
- 'title' => __( 'Recent Outbound Links', 'wp-slimstat' ),
532
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
533
- 'callback_args' => array(
534
- 'type' => 'recent',
535
- 'columns' => 'outbound_resource',
536
- 'raw' => array( 'wp_slimstat_db', 'get_recent_outbound' )
537
- ),
538
- 'classes' => array( 'large' ),
539
- 'screens' => array( 'slimview4' ),
540
- 'tooltip' => ''
541
- ),
542
- 'slim_p4_02' => array(
543
- 'title' => __( 'Recent Posts', 'wp-slimstat' ),
544
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
545
- 'callback_args' => array(
546
- 'type' => 'recent',
547
- 'columns' => 'resource',
548
- 'where' => 'content_type = "post"',
549
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
550
- ),
551
- 'classes' => array( 'normal' ),
552
- 'screens' => array( 'slimview4' )
553
- ),
554
- 'slim_p4_04' => array(
555
- 'title' => __( 'Recent Feeds', 'wp-slimstat' ),
556
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
557
- 'callback_args' => array(
558
- 'type' => 'recent',
559
- 'columns' => 'resource',
560
- 'where' => '(resource LIKE "%/feed%" OR resource LIKE "%?feed=>%" OR resource LIKE "%&feed=>%" OR content_type LIKE "%feed%")',
561
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
562
- ),
563
- 'classes' => array( 'normal', 'hidden' ),
564
- 'screens' => array( 'slimview4' )
565
- ),
566
- 'slim_p4_05' => array(
567
- 'title' => __( 'Recent Pages Not Found', 'wp-slimstat' ),
568
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
569
- 'callback_args' => array(
570
- 'type' => 'recent',
571
- 'columns' => 'resource',
572
- 'where' => '(resource LIKE "[404]%" OR content_type LIKE "%404%")',
573
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
574
- ),
575
- 'classes' => array( 'normal' ),
576
- 'screens' => array( 'slimview4' )
577
- ),
578
- 'slim_p4_06' => array(
579
- 'title' => __( 'Recent Internal Searches', 'wp-slimstat' ),
580
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
581
- 'callback_args' => array(
582
- 'type' => 'recent',
583
- 'columns' => 'searchterms',
584
- 'where' => 'content_type LIKE "%search%" AND searchterms <> "" AND searchterms IS NOT NULL',
585
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
586
- ),
587
- 'classes' => array( 'normal', 'hidden' ),
588
- 'screens' => array( 'slimview4' ),
589
- 'tooltip' => __( "Searches performed using WordPress' built-in search functionality.", 'wp-slimstat' )
590
- ),
591
- 'slim_p4_07' => array(
592
- 'title' => __( 'Top Categories', 'wp-slimstat' ),
593
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
594
- 'callback_args' => array(
595
- 'type' => 'top',
596
- 'columns' => 'category',
597
- 'where' => 'content_type LIKE "%category%"',
598
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
599
- ),
600
- 'classes' => array( 'normal' ),
601
- 'screens' => array( 'slimview4', 'dashboard' )
602
- ),
603
-
604
- 'slim_p4_09' => array(
605
- 'title' => __( 'Top Downloads', 'wp-slimstat' ),
606
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
607
- 'callback_args' => array(
608
- 'type' => 'top',
609
- 'columns' => 'resource',
610
- 'where' => 'content_type = "download"',
611
- 'raw' => array( 'wp_slimstat_db', 'get_top' ),
612
- 'criteria' => 'swap'
613
- ),
614
- 'classes' => array( 'large', 'hidden' ),
615
- 'screens' => array( 'slimview4' ),
616
- 'tooltip' => __( 'You can configure Slimstat to track specific file extensions as downloads.', 'wp-slimstat' )
617
- ),
618
- 'slim_p4_10' => array(
619
- 'title' => __( 'Recent Events', 'wp-slimstat' ),
620
- 'callback' => array( __CLASS__, 'show_events' ),
621
- 'callback_args' => array(
622
- 'type' => 'recent',
623
- 'columns' => 'notes',
624
- 'raw' => array( 'wp_slimstat_db', 'get_recent_events' )
625
- ),
626
- 'classes' => array( 'normal', 'hidden' ),
627
- 'screens' => array( 'slimview4' ),
628
- 'tooltip' => __( 'This report lists any <em>event</em> occurred on your website. Please refer to the FAQ for more information on how to use this functionality.', 'wp-slimstat' )
629
- ),
630
- 'slim_p4_11' => array(
631
- 'title' => __( 'Top Posts', 'wp-slimstat' ),
632
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
633
- 'callback_args' => array(
634
- 'type' => 'top',
635
- 'columns' => 'resource',
636
- 'where' => 'content_type = "post"',
637
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
638
- ),
639
- 'classes' => array( 'normal' ),
640
- 'screens' => array( 'slimview4' )
641
- ),
642
- 'slim_p4_12' => array(
643
- 'title' => __( 'Top Events', 'wp-slimstat' ),
644
- 'callback' => array( __CLASS__, 'show_events' ),
645
- 'callback_args' => array(
646
- 'type' => 'top',
647
- 'columns' => 'notes',
648
- 'raw' => array( 'wp_slimstat_db', 'get_top_events' )
649
- ),
650
- 'classes' => array( 'normal', 'hidden' ),
651
- 'screens' => array( 'slimview4' ),
652
- 'tooltip' => __( 'This report lists any <em>event</em> occurred on your website. Please refer to the FAQ for more information on how to use this functionality.', 'wp-slimstat' )
653
- ),
654
- 'slim_p4_13' => array(
655
- 'title' => __( 'Top Internal Searches', 'wp-slimstat' ),
656
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
657
- 'callback_args' => array(
658
- 'type' => 'top',
659
- 'columns' => 'searchterms',
660
- 'where' => 'content_type LIKE "%search%" AND searchterms <> "" AND searchterms IS NOT NULL',
661
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
662
- ),
663
- 'classes' => array( 'normal', 'hidden' ),
664
- 'screens' => array( 'slimview4' )
665
- ),
666
- 'slim_p4_15' => array(
667
- 'title' => __( 'Recent Categories', 'wp-slimstat' ),
668
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
669
- 'callback_args' => array(
670
- 'type' => 'recent',
671
- 'columns' => 'resource',
672
- 'where' => '(content_type = "category" OR content_type = "tag")',
673
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
674
- ),
675
- 'classes' => array( 'normal', 'hidden' ),
676
- 'screens' => array( 'slimview4' )
677
- ),
678
- 'slim_p4_16' => array(
679
- 'title' => __( 'Top Pages Not Found', 'wp-slimstat' ),
680
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
681
- 'callback_args' => array(
682
- 'type' => 'top',
683
- 'columns' => 'resource',
684
- 'where' => 'content_type LIKE "%404%"',
685
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
686
- ),
687
- 'classes' => array( 'normal' ),
688
- 'screens' => array( 'slimview4' )
689
- ),
690
- 'slim_p4_18' => array(
691
- 'title' => __( 'Top Authors', 'wp-slimstat' ),
692
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
693
- 'callback_args' => array(
694
- 'type' => 'top',
695
- 'columns' => 'author',
696
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
697
- ),
698
- 'classes' => array( 'normal' ),
699
- 'screens' => array( 'slimview4', 'dashboard' )
700
- ),
701
- 'slim_p4_19' => array(
702
- 'title' => __( 'Top Tags', 'wp-slimstat' ),
703
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
704
- 'callback_args' => array(
705
- 'type' => 'top',
706
- 'columns' => 'category',
707
- 'where' => '(content_type LIKE "%tag%")',
708
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
709
- ),
710
- 'classes' => array( 'normal', 'hidden' ),
711
- 'screens' => array( 'slimview4' )
712
- ),
713
- 'slim_p4_20' => array(
714
- 'title' => __( 'Recent Downloads', 'wp-slimstat' ),
715
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
716
- 'callback_args' => array(
717
- 'type' => 'recent',
718
- 'columns' => 'resource',
719
- 'where' => 'content_type = "download"',
720
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
721
- ),
722
- 'classes' => array( 'large', 'hidden' ),
723
- 'screens' => array( 'slimview4' )
724
- ),
725
- 'slim_p4_21' => array(
726
- 'title' => __( 'Top Outbound Links', 'wp-slimstat' ),
727
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
728
- 'callback_args' => array(
729
- 'type' => 'top',
730
- 'columns' => 'outbound_resource',
731
- 'raw' => array( 'wp_slimstat_db', 'get_top_outbound' ),
732
- 'criteria' => 'swap'
733
- ),
734
- 'classes' => array( 'normal', 'hidden' ),
735
- 'screens' => array( 'slimview4', 'dashboard' ),
736
- ),
737
- 'slim_p4_22' => array(
738
- 'title' => __( 'Your Website', 'wp-slimstat' ),
739
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
740
- 'callback_args' => array(
741
- 'raw' => array( __CLASS__, 'get_your_blog' )
742
- ),
743
- 'classes' => array( 'normal', 'hidden' ),
744
- 'screens' => array( 'slimview4' ),
745
- 'tooltip' => __( 'Your content at a glance: posts, comments, pingbacks, etc. Please note that this report is not affected by the filters set here above.', 'wp-slimstat' )
746
- ),
747
- 'slim_p4_23' => array(
748
- 'title' => __( 'Top Bounce Pages', 'wp-slimstat' ),
749
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
750
- 'callback_args' => array(
751
- 'type' => 'top',
752
- 'columns' => 'resource',
753
- 'where' => 'content_type <> "404"',
754
- 'having' => 'HAVING COUNT(visit_id) = 1',
755
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
756
- ),
757
- 'classes' => array( 'normal', 'hidden' ),
758
- 'screens' => array( 'slimview4' )
759
- ),
760
- 'slim_p4_24' => array(
761
- 'title' => __( 'Top Exit Pages', 'wp-slimstat' ),
762
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
763
- 'callback_args' => array(
764
- 'type' => 'top',
765
- 'columns' => 'visit_id',
766
- 'outer_select_column' => 'resource',
767
- 'aggr_function' => 'MAX',
768
- 'raw' => array( 'wp_slimstat_db', 'get_top_aggr' )
769
- ),
770
- 'classes' => array( 'large', 'hidden' ),
771
- 'screens' => array( 'slimview4', 'dashboard' )
772
- ),
773
- 'slim_p4_25' => array(
774
- 'title' => __( 'Top Entry Pages', 'wp-slimstat' ),
775
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
776
- 'callback_args' => array(
777
- 'type' => 'top',
778
- 'columns' => 'visit_id',
779
- 'outer_select_column' => 'resource',
780
- 'aggr_function' => 'MIN',
781
- 'raw' => array( 'wp_slimstat_db', 'get_top_aggr' )
782
- ),
783
- 'classes' => array( 'large', 'hidden' ),
784
- 'screens' => array( 'slimview4' )
785
- ),
786
- 'slim_p4_26_01' => array( // Chart Reports need to always have a _01 suffix to tell our custom "refresh" code to avoid fading the chart, which apparently doesn't work
787
- 'title' => __( 'Pages with Outbound Links', 'wp-slimstat' ),
788
- 'callback' => array( __CLASS__, 'show_chart' ),
789
- 'callback_args' => array(
790
- 'id' => 'slim_p4_26_01',
791
- 'chart_data' => array(
792
- 'data1' => 'COUNT( outbound_resource )',
793
- 'data2' => 'COUNT( DISTINCT outbound_resource )'
794
- ),
795
- 'chart_labels' => array(
796
- __( 'Outbound Links', 'wp-slimstat' ),
797
- __( 'Unique Outbound', 'wp-slimstat' )
798
- )
799
- ),
800
- 'classes' => array( 'extralarge', 'chart' ),
801
- 'screens' => array( 'slimview4' ),
802
- 'tooltip' => $chart_tooltip
803
- ),
804
-
805
- 'slim_p6_01' => array(
806
- 'title' => __( 'World Map', 'wp-slimstat' ),
807
- 'callback' => array( __CLASS__, 'show_world_map' ),
808
- 'callback_args' => array(
809
- 'id' => 'slim_p6_01'
810
- ),
811
- 'classes' => array( 'full-width', 'tall' ),
812
- 'screens' => array( 'slimview6' ),
813
- 'tooltip' => __( 'Dots on the map represent the most recent pageviews geolocated by City. This feature is only available by enabling the corresponding precision level in the settings.', 'wp-slimstat' )
814
- )
815
- );
816
-
817
- if ( wp_slimstat::$settings[ 'geolocation_country' ] != 'on' ) {
818
- self::$reports_info [ 'slim_p2_23' ] = array(
819
- 'title' => __( 'Top Cities', 'wp-slimstat' ),
820
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
821
- 'callback_args' => array(
822
- 'type' => 'top',
823
- 'columns' => 'city',
824
-
825
- 'raw' => array( 'wp_slimstat_db', 'get_top' )
826
- ),
827
- 'classes' => array( 'normal' ),
828
- 'screens' => array( 'slimview3' )
829
- );
830
- }
831
-
832
- // Allow third party tools to manipulate this list here above: please use unique report IDs that don't interfere with built-in ones, if you add your own custom report
833
- self::$reports_info = apply_filters( 'slimstat_reports_info', self::$reports_info );
834
-
835
- // Define what reports have been deprecated over time, to remove them from the user's settings
836
- $deprecated_reports = array(
837
- 'slim_p1_05' => 1,
838
- 'slim_p1_18' => 1,
839
- 'slim_p2_10' => 1,
840
- 'slim_p3_03' => 1,
841
- 'slim_p3_04' => 1,
842
- 'slim_p3_05' => 1,
843
- 'slim_p3_08' => 1,
844
- 'slim_p3_09' => 1,
845
- 'slim_p3_10' => 1,
846
- 'slim_p4_08' => 1,
847
- 'slim_p4_14' => 1,
848
- 'slim_p4_17' => 1,
849
- 'slim_getsocial' => 1
850
- );
851
-
852
- // Retrieve this user's list of active reports,
853
- $current_user = wp_get_current_user();
854
- $page_location = ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'on' ) ? 'slimstat' : 'admin';
855
-
856
- // Superadmins can customize the layout at network level, to override per-site settings
857
- self::$user_reports = get_user_option( "meta-box-order_slimstat_page_slimlayout-network", 1 );
858
-
859
- // No network-wide settings exist
860
- if ( empty( self::$user_reports ) ) {
861
- self::$user_reports = get_user_option( "meta-box-order_slimstat_page_slimlayout", $current_user->ID );
862
- }
863
-
864
- // Do this only if we are in one of our screens (no dashboard!)
865
- if ( is_admin() && !empty( $_REQUEST[ 'page' ] ) && strpos( $_REQUEST[ 'page' ], 'slimview' ) !== false ) {
866
-
867
- // If this list is not empty, we rearrange the order of our reports
868
- if ( !empty( self::$user_reports[ $_REQUEST[ 'page' ] ] ) ) {
869
- $user_reports_intersect = array_flip( explode( ',', self::$user_reports[ $_REQUEST[ 'page' ] ] ) );
870
- self::$reports_info = array_intersect_key( array_merge( $user_reports_intersect, self::$reports_info ), $user_reports_intersect );
871
- }
872
- else {
873
- foreach ( self::$reports_info as $a_report_id => $a_report_info ) {
874
- if ( !in_array( $_REQUEST[ 'page' ], $a_report_info[ 'screens' ] ) ) {
875
- unset( self::$reports_info[ $a_report_id ] );
876
- }
877
- }
878
- }
879
-
880
- // Remove deprecated reports
881
- self::$reports_info = array_diff_key( self::$reports_info, $deprecated_reports );
882
-
883
- $hidden_reports = get_user_option( "metaboxhidden_{$page_location}_page_{$_REQUEST['page']}", $current_user->ID );
884
-
885
- // If this list is not empty, use it instead of the predefined visibility
886
- if ( is_array( $hidden_reports ) ) {
887
- foreach ( self::$reports_info as $a_report_id => $a_report_info ) {
888
- if ( in_array( $a_report_id, $hidden_reports ) ) {
889
- if ( is_array( self::$reports_info[ $a_report_id ][ 'classes' ] ) && !in_array( 'hidden', $a_report_info[ 'classes' ] ) ) {
890
- self::$reports_info[ $a_report_id ][ 'classes' ][] = 'hidden';
891
- }
892
- }
893
- else if ( is_array( self::$reports_info[ $a_report_id ][ 'classes' ] ) ) {
894
- self::$reports_info[ $a_report_id ][ 'classes' ] = array_diff( self::$reports_info[ $a_report_id ][ 'classes' ], array( 'hidden' ) );
895
- }
896
- }
897
- }
898
- }
899
- // If we are on the WP Dashboard page, all the reports are 'visible': WP will take care of honoring the Screen Options settings for that page
900
- else if ( !empty( $_REQUEST['page'] ) && strpos( $_REQUEST['page'], 'slimlayout' ) === false ) {
901
- foreach ( self::$reports_info as $a_report_id => $a_report_info ) {
902
- if ( is_array( self::$reports_info[ $a_report_id ][ 'classes' ] ) ) {
903
- self::$reports_info[ $a_report_id ][ 'classes' ] = array_diff( self::$reports_info[ $a_report_id ][ 'classes' ], array( 'hidden' ) );
904
- }
905
- }
906
- }
907
- }
908
- // end init
909
-
910
- public static function report_header( $_report_id = '' ) {
911
- if ( empty( self::$reports_info[ $_report_id ] ) ) {
912
- return false;
913
- }
914
-
915
- $header_classes = !empty( self::$reports_info[ $_report_id ][ 'classes' ] ) ? implode( ' ', self::$reports_info[ $_report_id ][ 'classes' ] ) : '';
916
- $header_buttons = '';
917
- $header_tooltip = '';
918
-
919
- // Don't show the header buttons on the frontend
920
- if ( is_admin() ) {
921
- // Show the refresh button only if the time range is not in the past
922
- if ( wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] >= date_i18n( 'U' ) - 300 ) {
923
- $header_buttons = '<a class="noslimstat refresh slimstat-font-arrows-cw" title="'.__('Refresh','wp-slimstat').'" href="'.self::fs_url().'"></a>';
924
- }
925
-
926
- // Allow third-party code to add more buttons
927
- $header_buttons = apply_filters( 'slimstat_report_header_buttons', $header_buttons, $_report_id );
928
- $header_buttons = '<div class="slimstat-header-buttons">' . $header_buttons . '</div>';
929
- $header_tooltip = !empty( self::$reports_info[ $_report_id ][ 'tooltip' ] ) ? '<i class="slimstat-tooltip-trigger corner"><span class="slimstat-tooltip-content">' . self::$reports_info[ $_report_id ][ 'tooltip' ] . '</span></i>' : '';
930
- }
931
-
932
- echo "<div class='postbox $header_classes' id='$_report_id'>{$header_buttons} <h3 data-report-id='{$_report_id}'>" . self::$reports_info[ $_report_id ][ 'title' ] . " {$header_tooltip}</h3><div class='inside'>";
933
- }
934
-
935
- public static function report_footer(){
936
- echo '</div></div>';
937
- }
938
-
939
- public static function report_pagination( $_count_page_results = 0, $_count_all_results = 0, $_show_refresh_countdown = false, $_results_per_page = -1 ) {
940
- if ( !is_admin() ) {
941
- return '';
942
- }
943
-
944
- $_results_per_page = ( $_results_per_page < 0 ) ? wp_slimstat::$settings[ 'rows_to_show' ] : $_results_per_page;
945
-
946
- $endpoint = min( $_count_all_results, wp_slimstat_db::$filters_normalized[ 'misc' ][ 'start_from' ] + $_results_per_page );
947
- $pagination_buttons = '';
948
- $direction_prev = is_rtl() ? 'right' : 'left';
949
- $direction_next = is_rtl() ? 'left' : 'right';
950
-
951
- if ( $endpoint + $_results_per_page < $_count_all_results && $_count_page_results > 0 ) {
952
- $startpoint = $_count_all_results - $_count_all_results % $_results_per_page;
953
- if ( $startpoint == $_count_all_results ) {
954
- $startpoint -= $_results_per_page;
955
- }
956
- $pagination_buttons .= '<a class="refresh slimstat-font-angle-double-' . $direction_next . '" href="' . wp_slimstat_reports::fs_url( 'start_from equals ' . $startpoint ) . '"></a> ';
957
- }
958
- if ($endpoint < $_count_all_results && $_count_page_results > 0){
959
- $startpoint = wp_slimstat_db::$filters_normalized['misc']['start_from'] + $_results_per_page;
960
- $pagination_buttons .= '<a class="refresh slimstat-font-angle-'.$direction_next.'" href="' . wp_slimstat_reports::fs_url( 'start_from equals ' . $startpoint ) . '"></a> ';
961
- }
962
- if (wp_slimstat_db::$filters_normalized['misc']['start_from'] > 0){
963
- $startpoint = (wp_slimstat_db::$filters_normalized['misc']['start_from'] > $_results_per_page)?wp_slimstat_db::$filters_normalized['misc']['start_from'] - $_results_per_page : 0;
964
- $pagination_buttons .= '<a class="refresh slimstat-font-angle-'.$direction_prev.'" href="'.wp_slimstat_reports::fs_url('start_from equals '.$startpoint).'"></a> ';
965
- }
966
- if (wp_slimstat_db::$filters_normalized['misc']['start_from'] - $_results_per_page > 0){
967
- $pagination_buttons .= '<a class="refresh slimstat-font-angle-double-'.$direction_prev.'" href="'.wp_slimstat_reports::fs_url('start_from equals 0').'"></a> ';
968
- }
969
-
970
- $pagination = '<p class="pagination">' . sprintf( __( 'Results %s - %s of %s', 'wp-slimstat' ), number_format( wp_slimstat_db::$filters_normalized[ 'misc' ][ 'start_from' ] + 1, 0, '', wp_slimstat_db::$formats[ 'thousand' ] ), number_format( $endpoint, 0, '', wp_slimstat_db::$formats[ 'thousand' ] ), number_format( $_count_all_results, 0, '', wp_slimstat_db::$formats[ 'thousand' ] ) . ( ( $_count_all_results == wp_slimstat::$settings[ 'limit_results' ] ) ? '+' : '') );
971
-
972
- if ( $_show_refresh_countdown && wp_slimstat::$settings[ 'refresh_interval' ] > 0 && wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] >= date_i18n( 'U' ) - 300 ) {
973
- $pagination .= ' [' . __( 'Refresh in', 'wp-slimstat' ) . ' <i class="refresh-timer"></i>]';
974
- }
975
- $pagination .= $pagination_buttons.'</p>';
976
-
977
- return $pagination;
978
- }
979
-
980
- public static function callback_wrapper() {
981
- $_args = self::_check_args( func_get_args() );
982
- call_user_func( $_args[ 'callback' ] , $_args[ 'callback_args' ] );
983
- }
984
-
985
- public static function raw_results_to_html( $_args = array() ) {
986
- if ( wp_slimstat::$settings[ 'async_load' ] == 'on' && ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
987
- return '';
988
- }
989
-
990
- wp_slimstat_db::$debug_message = '';
991
-
992
- $all_results = call_user_func( $_args[ 'raw' ] , $_args );
993
-
994
- echo wp_slimstat_db::$debug_message;
995
-
996
- // Some reports don't need any kind of pre/post-processing, we just display the data contained in the array
997
- if ( empty( $_args[ 'columns' ] ) ) {
998
- foreach ( $all_results as $a_result ) {
999
- echo '<p>';
1000
-
1001
- if ( !empty( $a_result[ 'tooltip' ] ) ) {
1002
- self::inline_help( $a_result[ 'tooltip' ] );
1003
- }
1004
-
1005
- echo "{$a_result[ 'metric' ]} <span>{$a_result[ 'value' ]}</span>";
1006
-
1007
- if ( !empty( $a_result[ 'details' ] ) ) {
1008
- $is_expanded = ( wp_slimstat::$settings[ 'expand_details' ] == 'on' ) ? ' expanded' : '';
1009
- echo "<b class='slimstat-tooltip-content$is_expanded'>{$a_result[ 'details' ]}</b>";
1010
- }
1011
-
1012
- echo '</p>';
1013
- }
1014
- }
1015
- else {
1016
- $results = array_slice(
1017
- $all_results,
1018
- wp_slimstat_db::$filters_normalized[ 'misc' ][ 'start_from' ],
1019
- wp_slimstat::$settings[ 'rows_to_show' ]
1020
- );
1021
-
1022
- // Count the results
1023
- $count_page_results = count( $results );
1024
-
1025
- if ($count_page_results == 0){
1026
- echo '<p class="nodata">' . __( 'No data to display', 'wp-slimstat' ) . '</p>';
1027
-
1028
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1029
- die();
1030
- }
1031
- else{
1032
- return array();
1033
- }
1034
- }
1035
-
1036
- // Some reports use aliases for column names
1037
- if ( !empty( $_args[ 'as_column' ] ) ) {
1038
- $_args[ 'columns' ] = $_args[ 'as_column' ];
1039
- }
1040
- else if ( !empty( $_args[ 'outer_select_column' ] ) ) {
1041
- $_args[ 'columns' ] = $_args[ 'outer_select_column' ];
1042
- }
1043
-
1044
- // Some reports query more than one column
1045
- if ( strpos( $_args[ 'columns' ], ',' ) !== false ) {
1046
- $_args[ 'columns' ] = explode( ',', $_args[ 'columns' ] );
1047
- $_args[ 'columns' ] = trim( $_args[ 'columns' ][ 0 ] );
1048
- }
1049
-
1050
- echo self::report_pagination( $count_page_results, count( $all_results ) );
1051
-
1052
- $is_expanded = ( is_admin() && wp_slimstat::$settings[ 'expand_details' ] == 'on' ) ? ' expanded' : '';
1053
- $permalinks_enabled = get_option( 'permalink_structure' );
1054
- $column_not_calculated = str_replace( '_calculated', '', $_args[ 'columns' ] );
1055
-
1056
- for($i=0; $i<$count_page_results; $i++){
1057
- $row_details = $percentage = '';
1058
- $element_pre_value = '';
1059
- $element_value = $results[ $i ][ $_args[ 'columns' ] ];
1060
-
1061
- // Some columns require a special pre-treatment
1062
- switch ( $column_not_calculated ){
1063
-
1064
- case 'browser':
1065
- if ( !empty( $results[ $i ][ 'user_agent' ] ) && wp_slimstat::$settings[ 'show_complete_user_agent_tooltip' ] == 'on' ) {
1066
- $element_pre_value = self::inline_help($results[$i]['user_agent'], false);
1067
- }
1068
- $element_value = $results[$i]['browser'].((isset($results[$i]['browser_version']) && intval($results[$i]['browser_version']) != 0)?' '.$results[$i]['browser_version']:'');
1069
- break;
1070
-
1071
- case 'category':
1072
- $row_details = __( 'Category ID', 'wp-slimstat' ) . ": {$results[ $i ][ $_args[ 'columns' ] ]}";
1073
- $element_value = get_cat_name( $results[ $i ][ $_args[ 'columns' ] ] );
1074
- break;
1075
-
1076
- case 'country':
1077
- $row_details .= __('Code','wp-slimstat').": {$results[$i]['country']}";
1078
- $element_value = __('c-'.$results[$i]['country'], 'wp-slimstat');
1079
- break;
1080
-
1081
- case 'ip':
1082
- if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'on' ) {
1083
- $element_value = gethostbyaddr( $results[ $i ][ $_args[ 'columns' ] ] );
1084
- }
1085
- else{
1086
- $element_value = $results[ $i ][ $_args[ 'columns' ] ];
1087
- }
1088
- break;
1089
-
1090
- case 'language':
1091
- $row_details = __( 'Code', 'wp-slimstat' ) . ": {$results[ $i ][ $_args[ 'columns' ] ]}";
1092
- $element_value = __( 'l-' . $results[ $i ][ $_args[ 'columns' ] ], 'wp-slimstat' );
1093
- break;
1094
-
1095
- case 'platform':
1096
- $row_details = __( 'Code', 'wp-slimstat' ).": {$results[ $i ][ $_args[ 'columns' ] ]}";
1097
- $element_value = __( $results[ $i ][ $_args[ 'columns' ] ], 'wp-slimstat' );
1098
- $results[ $i ][ $_args[ 'columns' ] ] = str_replace( 'p-', '', $results[ $i ][ $_args[ 'columns' ] ] );
1099
- break;
1100
-
1101
- case 'referer':
1102
- $element_value = str_replace( array( '<', '>' ), array( '&lt;', '&gt;' ), urldecode( $results[ $i ][ $_args[ 'columns' ] ] ) );
1103
- //$element_value = parse_url( $element_value, PHP_URL_HOST );
1104
- break;
1105
-
1106
- case 'resource':
1107
- $resource_title = self::get_resource_title( $results[ $i ][ $_args[ 'columns' ] ] );
1108
- if ( $resource_title != $results[ $i ][ $_args[ 'columns' ] ] ) {
1109
- $row_details = __( 'URL', 'wp-slimstat' ) . ': ' . htmlentities( $results[ $i ][ $_args[ 'columns' ] ], ENT_QUOTES, 'UTF-8' );
1110
- }
1111
- if ( !empty( $_args[ 'where' ] ) && strpos( $_args[ 'where' ], 'download' ) !== false ) {
1112
- $clean_extension = pathinfo( strtolower( parse_url( $results[ $i ][ $_args[ 'columns' ] ], PHP_URL_PATH ) ), PATHINFO_EXTENSION );
1113
- if ( in_array( $clean_extension, array( 'jpg', 'gif', 'png', 'jpeg', 'bmp' ) ) ) {
1114
- $row_details = '<br><img src="' . $results[ $i ][ $_args[ 'columns' ] ] . '" style="width:100px">';
1115
- }
1116
- }
1117
- $element_value = $resource_title;
1118
- break;
1119
-
1120
- case 'screen_width':
1121
- $element_value = "{$results[ $i ][ $_args[ 'columns' ] ]} x {$results[ $i ][ 'screen_height' ]}";
1122
- break;
1123
-
1124
- case 'searchterms':
1125
- if ( $_args[ 'type' ] == 'recent' ) {
1126
- $domain = parse_url( $results[ $i ][ 'referer' ], PHP_URL_HOST );
1127
-
1128
- $row_details = __( 'Referrer', 'wp-slimstat' ) . ": $domain";
1129
- $element_value = self::get_search_terms_info( $results[ $i ][ 'searchterms' ], $results[ $i ][ 'referer' ], true );
1130
- }
1131
- else{
1132
- $element_value = htmlentities( $results[ $i ][ 'searchterms' ], ENT_QUOTES, 'UTF-8' );
1133
- }
1134
- break;
1135
-
1136
- case 'username':
1137
- $element_value = $results[ $i ][ 'username' ];
1138
- if ( wp_slimstat::$settings[ 'show_display_name' ] == 'on' ) {
1139
- $element_custom_value = get_user_by( 'login', $results[ $i ][ 'username' ] );
1140
- if ( is_object( $element_custom_value ) ) {
1141
- $element_value = $element_custom_value->display_name;
1142
- }
1143
- }
1144
- break;
1145
-
1146
- case 'visit_id':
1147
- $resource_title = self::get_resource_title( $results[ $i ][ 'resource' ] );
1148
- if ( $resource_title != $results[ $i ][ 'resource' ] ) {
1149
- $row_details = htmlentities( $results[ $i ][ 'resource' ], ENT_QUOTES, 'UTF-8' );
1150
- }
1151
- $element_value = $resource_title;
1152
- break;
1153
- default:
1154
- }
1155
-
1156
- if ( is_admin() ) {
1157
- $element_value = "<a class='slimstat-filter-link' href='" . self::fs_url( $column_not_calculated. ' ' . $_args[ 'filter_op' ] . ' ' . $results[ $i ][ $_args[ 'columns' ] ] ) . "'>$element_value</a>";
1158
- }
1159
-
1160
- if ( !empty( $_args['type'] ) && $_args['type'] == 'recent' ) {
1161
- $row_details = date_i18n(wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ], $results[ $i ][ 'dt' ], true ) . ( !empty( $row_details ) ? '<br>' : '' ) . $row_details;
1162
- }
1163
-
1164
- if ( !empty($_args[ 'type' ] ) && $_args[ 'type' ] == 'top' ) {
1165
- $percentage_value = ( ( self::$pageviews > 0 ) ? number_format( sprintf( "%01.2f", ( 100 * $results[ $i ][ 'counthits' ] / self::$pageviews ) ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 );
1166
- $counthits = number_format( $results[ $i ][ 'counthits' ], 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1167
-
1168
- if ( !empty( $_args[ 'criteria' ] ) && $_args[ 'criteria' ] == 'swap' ) {
1169
- $percentage = ' <span>' . $counthits . '</span>';
1170
- $row_details = __('Hits','wp-slimstat') . ': ' . ( ( $column_not_calculated != 'outbound_resource' ) ? $percentage_value . '%' . ( !empty( $row_details ) ? '<br>' : '' ) . $row_details : '' );
1171
- }
1172
- else {
1173
- $percentage = ' <span>' . $percentage_value . '%</span>';
1174
- $row_details = __('Hits','wp-slimstat') . ': ' . $counthits . ( !empty( $row_details ) ? '<br>' : '' ) . $row_details;
1175
- }
1176
- }
1177
-
1178
- // Some columns require a special post-treatment
1179
- if ( $_args[ 'columns' ] == 'resource' && strpos( $_args['where'], '404' ) === false ) {
1180
- $base_url = '';
1181
- if (isset($results[$i]['blog_id'])){
1182
- $base_url = parse_url(get_site_url($results[$i]['blog_id']));
1183
- $base_url = $base_url['scheme'].'://'.$base_url['host'];
1184
- }
1185
- $element_value = '<a target="_blank" class="slimstat-font-logout" title="'.__('Open this URL in a new window','wp-slimstat').'" href="'.$base_url.htmlentities($results[$i]['resource'], ENT_QUOTES, 'UTF-8').'"></a> '.$base_url.$element_value;
1186
- }
1187
-
1188
- if ( $_args[ 'columns' ] == 'referer_calculated' && !empty( $_args[ 'type' ] ) && $_args[ 'type' ] == 'top' ) {
1189
- $element_url = 'http://' . htmlentities( $results[ $i ][ 'referer_calculated' ], ENT_QUOTES, 'UTF-8' );
1190
- $element_value = '<a target="_blank" class="slimstat-font-logout" title="'.__('Open this URL in a new window','wp-slimstat').'" href="'.$element_url.'"></a> '.$element_value;
1191
- }
1192
-
1193
- if ( $_args[ 'columns' ] == 'referer' && !empty( $_args[ 'type' ] ) && $_args[ 'type' ] == 'top' ) {
1194
- $element_url = htmlentities( $results[ $i ][ 'referer' ], ENT_QUOTES, 'UTF-8' );
1195
- $element_value = '<a target="_blank" class="slimstat-font-logout" title="'.__('Open this URL in a new window','wp-slimstat').'" href="'.$element_url.'"></a> '.$element_value;
1196
- }
1197
-
1198
- if ( is_admin() && !empty( $results[ $i ][ 'ip' ]) && $_args[ 'columns' ] != 'ip' && wp_slimstat::$settings[ 'convert_ip_addresses' ] != 'on' ) {
1199
- $row_details .= '<br> IP: <a class="slimstat-filter-link" href="'.self::fs_url( 'ip equals ' . $results[ $i ][ 'ip' ] ) . '">' . $results[ $i ][ 'ip' ] . '</a>' . ( !empty( $results[ $i ][ 'other_ip' ] ) ? ' / ' . $results[ $i ][ 'other_ip' ] : '' ) . '<a title="WHOIS: ' . $results[ $i ][ 'ip' ] . '" class="slimstat-font-location-1 whois" href="' . wp_slimstat::$settings[ 'ip_lookup_service' ] . $results[ $i ][ 'ip' ] . '"></a>';
1200
- }
1201
- if ( !empty( $row_details ) ) {
1202
- $row_details = "<b class='slimstat-tooltip-content$is_expanded'>$row_details</b>";
1203
- }
1204
-
1205
- $row_output = "<p class='slimstat-tooltip-trigger'>$element_pre_value$element_value$percentage $row_details</p>";
1206
-
1207
- // Strip all the filter links, if this information is shown on the frontend
1208
- if ( !is_admin() ) {
1209
- $row_output = preg_replace('/<a (.*?)>(.*?)<\/a>/', "\\2", $row_output);
1210
- }
1211
-
1212
- echo $row_output;
1213
- }
1214
- }
1215
-
1216
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1217
- die();
1218
- }
1219
- }
1220
-
1221
- public static function show_activity_log( $_args = array() ) {
1222
- // This function is too long, so it was moved to a separate file
1223
- include( WP_PLUGIN_DIR . '/wp-slimstat/admin/view/right-now.php' );
1224
-
1225
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1226
- die();
1227
- }
1228
- }
1229
-
1230
- public static function show_chart( $_args = array() ) {
1231
- $chart_data = wp_slimstat_db::get_data_for_chart( $_args[ 'chart_data' ] );
1232
-
1233
- if ( empty( $chart_data[ 'json_count' ] ) ) {
1234
- if ( !empty( wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval' ] ) && wp_slimstat_db::$filters_normalized[ 'date' ][ 'interval' ] == 1 ) {
1235
- echo '<p class="nodata">' . __( 'Chart is not displayed when the selected time range is comprised of a single day', 'wp-slimstat') . '</p>';
1236
- }
1237
- else {
1238
- echo '<p class="nodata">' . __( 'No data to display', 'wp-slimstat') . '</p>';
1239
- }
1240
-
1241
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1242
- die();
1243
- }
1244
- else {
1245
- return 0;
1246
- }
1247
- }
1248
-
1249
- // Enqueue all the Javascript and styles
1250
- $path_slimstat = dirname( dirname( __FILE__ ) );
1251
- wp_enqueue_script( 'slimstat_amcharts', plugins_url( '/admin/js/amcharts/amcharts.js', $path_slimstat ), array(), null, false );
1252
- wp_enqueue_script( 'slimstat_amcharts_serial', plugins_url( '/admin/js/amcharts/serial.js', $path_slimstat ), array( 'slimstat_amcharts' ), null, false );
1253
- wp_enqueue_script( 'slimstat_amcharts_plugins_export', plugins_url( '/admin/js/amcharts/plugins/export/export.min.js', $path_slimstat ), array( 'slimstat_amcharts' ), null, false );
1254
- wp_enqueue_script( 'slimstat_amcharts_theme_light', plugins_url( '/admin/js/amcharts/themes/light.js', $path_slimstat ), array( 'slimstat_amcharts' ), null, false );
1255
-
1256
- wp_enqueue_style( 'slimstat_amcharts_plugins_export_css', plugins_url( '/admin/js/amcharts/plugins/export/export.css', $path_slimstat ) );
1257
-
1258
- // Pre-calculate all the values needed to render the charts
1259
-
1260
- $chart_colors = !empty( wp_slimstat::$settings[ 'chart_colors' ] ) ? wp_slimstat::string_to_array( wp_slimstat::$settings[ 'chart_colors' ] ) : array( '#bbcc44', '#21759b', '#ccc', '#999' );
1261
-
1262
- ?>
1263
- <div class="chart-placeholder" id="chart_<?php echo $_args[ 'id' ]; ?>" style="min-height: 280px"></div>
1264
-
1265
- <script type="text/javascript">
1266
- <?php if ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ): ?>
1267
- jQuery(function() {
1268
- <?php endif; ?>
1269
- var chart_<?php echo $_args[ 'id' ]; ?> = AmCharts.makeChart("chart_<?php echo $_args[ 'id' ]; ?>", {
1270
- "type": "serial",
1271
- "zoomOutButtonPadding": 25,
1272
- "theme": "light",
1273
- "creditsPosition": "top-left",
1274
- "mouseWheelZoomEnabled": true,
1275
- "legend": {
1276
- "equalWidths": true,
1277
- "periodValueText": "[[value.average]]",
1278
- "position": "bottom",
1279
- "align": "center"
1280
- },
1281
- "valueAxes": [ {
1282
- "id":"v1",
1283
- "axisAlpha": 0,
1284
- "integersOnly": true,
1285
- "position": "left"
1286
- } ],
1287
- "chartCursor": {
1288
- "cursorPosition": "mouse",
1289
- "pan": true,
1290
- "valueLineBalloonEnabled": true
1291
- },
1292
- "categoryField": "date",
1293
- "categoryAxis": {
1294
- "dashLength": 1,
1295
- "autoGridCount": <?php echo ( $chart_data[ 'json_count' ] > 31 || wp_slimstat_db::$filters_normalized[ 'utime' ][ 'type' ] == 'interval' || empty( $_REQUEST[ 'page' ] ) ) ? 'true' : 'false' ?>,
1296
- "gridCount": <?php echo $chart_data[ 'json_count' ] ?>,
1297
- "position": "bottom"
1298
- },
1299
- "export": {
1300
- "enabled": true
1301
- },
1302
- "numberFormatter": {
1303
- "precision": 0
1304
- },
1305
- "graphs": [
1306
- <?php if ( !empty( $chart_data[ 'previous' ][ 'label' ] ) ) : ?>
1307
- {
1308
- "id": "g3_<?php echo $_args[ 'id' ]; ?>",
1309
- "bullet": "round",
1310
- "bulletBorderAlpha": 1,
1311
- "bulletColor": "#00FF00",
1312
- "bulletSize": 4,
1313
- "hideBulletsCount": 50,
1314
- "lineThickness": 2,
1315
- "lineColor": "<?php echo $chart_colors[ 2 ] ?>",
1316
- "title": "<?php echo htmlspecialchars( $_args[ 'chart_labels' ][ 0 ] . ' ' . $chart_data[ 'previous' ][ 'label' ], ENT_QUOTES, 'UTF-8' ); ?> (avg)",
1317
- "type": "line",
1318
- "useLineColorForBulletBorder": true,
1319
- "valueField": "v3"
1320
- }, {
1321
- "id": "g4_<?php echo $_args[ 'id' ]; ?>",
1322
- "bullet": "round",
1323
- "bulletBorderAlpha": 1,
1324
- "bulletColor": "#00FF00",
1325
- "bulletSize": 4,
1326
- "hideBulletsCount": 50,
1327
- "lineThickness": 2,
1328
- "lineColor": "<?php echo $chart_colors[ 3 ] ?>",
1329
- "title": "<?php echo htmlspecialchars( $_args[ 'chart_labels' ][ 1 ] . ' ' . $chart_data[ 'previous' ][ 'label' ], ENT_QUOTES, 'UTF-8' ); ?> (avg)",
1330
- "type": "line",
1331
- "useLineColorForBulletBorder": true,
1332
- "valueField": "v4"
1333
- },
1334
- <?php endif; ?>
1335
- {
1336
- "id": "g1_<?php echo $_args[ 'id' ]; ?>",
1337
- "bullet": "round",
1338
- "bulletBorderAlpha": 1,
1339
- "bulletColor": "#FFFFFF",
1340
- "bulletSize": 4,
1341
- "hideBulletsCount": 50,
1342
- "lineColor": "<?php echo $chart_colors[ 0 ] ?>",
1343
- "lineThickness": 2,
1344
- "title": "<?php echo htmlspecialchars( $_args[ 'chart_labels' ][ 0 ] . ' ' . $chart_data[ 'current' ][ 'label' ], ENT_QUOTES, 'UTF-8' ); ?> (avg)",
1345
- "type": "line",
1346
- "useLineColorForBulletBorder": true,
1347
- "valueField": "v1"
1348
- }, {
1349
- "id": "g2_<?php echo $_args[ 'id' ]; ?>",
1350
- "bullet": "round",
1351
- "bulletBorderAlpha": 0.6,
1352
- "bulletColor": "#00FF00",
1353
- "bulletSize": 4,
1354
- "hideBulletsCount": 50,
1355
- "lineColor": "<?php echo $chart_colors[ 1 ] ?>",
1356
- "lineThickness": 2,
1357
- "title": "<?php echo htmlspecialchars( $_args[ 'chart_labels' ][ 1 ] . ' ' . $chart_data[ 'current' ][ 'label' ], ENT_QUOTES, 'UTF-8' ); ?> (avg)",
1358
- "type": "line",
1359
- "useLineColorForBulletBorder": true,
1360
- "valueField": "v2"
1361
- }
1362
-
1363
- ],
1364
- "dataProvider": <?php echo $chart_data[ 'json' ] ?>
1365
- });
1366
- <?php if ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ): ?>
1367
- });
1368
- <?php endif; ?>
1369
- </script>
1370
- <?php
1371
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1372
- die();
1373
- }
1374
- }
1375
-
1376
- public static function get_about_wpslimstat() {
1377
- $dt = wp_slimstat_db::get_oldest_visit( '1=1', false );
1378
- $results = array();
1379
-
1380
- $results[ 0 ][ 'metric' ] = __( 'Dataset Size', 'wp-slimstat' );
1381
- $results[ 0 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', '1=1', false ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1382
- $results[ 0 ][ 'tooltip' ] = __( 'Total number of records stored in the database.', 'wp-slimstat' );
1383
-
1384
- $results[ 1 ][ 'metric' ] = __( 'DB Size', 'wp-slimstat' );
1385
- $results[ 1 ][ 'value' ] = wp_slimstat_db::get_data_size();
1386
-
1387
- $results[ 2 ][ 'metric' ] = __( 'Tracking Enabled', 'wp-slimstat' );
1388
- $results[ 2 ][ 'value' ] = __( ucfirst( wp_slimstat::$settings[ 'is_tracking' ] ), 'wp-slimstat' );
1389
-
1390
- $results[ 3 ][ 'metric' ] = __( 'Javascript Mode', 'wp-slimstat' );
1391
- $results[ 3 ][ 'value' ] = __( ucfirst( wp_slimstat::$settings[ 'javascript_mode' ] ), 'wp-slimstat' );
1392
-
1393
- $results[ 4 ][ 'metric' ] = __( 'Tracking Browser Caps', 'wp-slimstat' );
1394
- $results[ 4 ][ 'value' ] = __( ucfirst( wp_slimstat::$settings[ 'enable_javascript' ] ), 'wp-slimstat' );
1395
-
1396
- $results[ 5 ][ 'metric' ] = __( 'Auto purge', 'wp-slimstat' );
1397
- $results[ 5 ][ 'value' ] = ( wp_slimstat::$settings[ 'auto_purge' ] > 0 ) ? wp_slimstat::$settings[ 'auto_purge' ] . ' ' . __( 'days', 'wp-slimstat' ) : __( 'Off', 'wp-slimstat' );
1398
-
1399
- $results[ 6 ][ 'metric' ] = __( 'Oldest pageview', 'wp-slimstat' );
1400
- $results[ 6 ][ 'value' ] = ( $dt == null ) ? __( 'No visits', 'wp-slimstat' ) : date_i18n( wp_slimstat::$settings[ 'date_format' ], $dt );
1401
-
1402
- $results[ 7 ][ 'metric' ] = __( 'Geolocation', 'wp-slimstat' );
1403
- $results[ 7 ][ 'value' ] = date_i18n( wp_slimstat::$settings[ 'date_format' ], @filemtime( wp_slimstat::$maxmind_path ) );
1404
- $results[ 7 ][ 'tooltip' ] = __( 'Date when the MaxMind Geolocation database was last updated.', 'wp-slimstat' );
1405
-
1406
- return $results;
1407
- }
1408
-
1409
- public static function get_overview_summary() {
1410
- $days_in_range = ceil( ( wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] - wp_slimstat_db::$filters_normalized[ 'utime' ][ 'start' ] ) / 86400 );
1411
- $results = array();
1412
-
1413
- $results[ 0 ][ 'metric' ] = __( 'Pageviews', 'wp-slimstat' );
1414
- $results[ 0 ][ 'value' ] = number_format( self::$pageviews, 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1415
- $results[ 0 ][ 'tooltip' ] = __( 'A request to load a single HTML file. Slimstat logs a "pageview" each time the tracking code is executed.', 'wp-slimstat' );
1416
-
1417
- $results[ 1 ][ 'metric' ] = __( 'Days in Range', 'wp-slimstat' );
1418
- $results[ 1 ][ 'value' ] = $days_in_range;
1419
-
1420
- $results[ 2 ][ 'metric' ] = __( 'Average Daily Pageviews', 'wp-slimstat' );
1421
- $results[ 2 ][ 'value' ] = number_format( round( self::$pageviews / $days_in_range, 0 ), 0, '', wp_slimstat_db::$formats['thousand'] );
1422
- $results[ 2 ][ 'tooltip' ] = __( 'How many pages have been visited on average every day during the current period.', 'wp-slimstat' );
1423
-
1424
- $results[ 3 ][ 'metric' ] = __( 'From Search Results', 'wp-slimstat' );
1425
- $results[ 3 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'searchterms IS NOT NULL' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1426
- $results[ 3 ][ 'tooltip' ] = __( 'Visitors who landed on your site after searching for a keyword on Google, Yahoo, etc.', 'wp-slimstat' );
1427
-
1428
- $results[ 4 ][ 'metric' ] = __( 'Unique IPs', 'wp-slimstat' );
1429
- $results[ 4 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'ip' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1430
- $results[ 4 ][ 'tooltip' ] = __( 'Used to differentiate between multiple requests to download a file from one internet address (IP) and requests originating from many distinct addresses', 'wp-slimstat' );
1431
-
1432
- $results[ 5 ][ 'metric' ] = __( 'Last 30 minutes', 'wp-slimstat' );
1433
- $results[ 5 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'dt > ' . ( date_i18n( 'U' ) - 1800 ), false ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1434
-
1435
- $results[ 6 ][ 'metric' ] = __( 'Today', 'wp-slimstat' );
1436
- $results[ 6 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'dt > ' . ( date_i18n( 'U', mktime( 0, 0, 0, date_i18n( 'm' ), date_i18n( 'd' ), date_i18n( 'Y' ) ) ) ), false ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1437
-
1438
- $results[ 7 ][ 'metric' ] = __( 'Yesterday', 'wp-slimstat' );
1439
- $results[ 7 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'dt BETWEEN ' . ( date_i18n( 'U', mktime( 0, 0, 0, date_i18n( 'm' ), date_i18n( 'd' ) - 1, date_i18n( 'Y' ) ) ) ) . ' AND ' . ( date_i18n( 'U', mktime( 23, 59, 59, date_i18n( 'm' ), date_i18n( 'd' ) - 1, date_i18n( 'Y' ) ) ) ), false ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1440
-
1441
- return $results;
1442
- }
1443
-
1444
- public static function get_plugins() {
1445
- $wp_slim_plugins = array( 'flash', 'silverlight', 'acrobat', 'java', 'mediaplayer', 'director', 'real', 'quicktime' );
1446
- $total_human_hits = wp_slimstat_db::count_records( 'id', 'visit_id > 0 AND browser_type <> 1' );
1447
- $results = array();
1448
-
1449
- foreach ( $wp_slim_plugins as $i => $a_plugin ) {
1450
- $count_results = wp_slimstat_db::count_records( 'id', "plugins LIKE '%{$a_plugin}%'" );
1451
- $results[ $i ][ 'metric' ] = ucfirst( $a_plugin );
1452
- $results[ $i ][ 'value' ] = ( $total_human_hits > 0 ) ? number_format( ( 100 * $count_results / $total_human_hits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0;
1453
- $results[ $i ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1454
- }
1455
-
1456
- return $results;
1457
- }
1458
-
1459
- public static function get_visitors_summary() {
1460
- $results = array();
1461
- $total_human_hits = wp_slimstat_db::count_records( 'id', 'visit_id > 0 AND browser_type <> 1');
1462
- $new_visitors = wp_slimstat_db::count_records_having( 'ip', 'visit_id > 0 AND browser_type <> 1', 'COUNT(visit_id) = 1' );
1463
- $new_visitors_rate = ( $total_human_hits > 0) ? ( 100 * $new_visitors / $total_human_hits ) : 0;
1464
- $metrics_per_visit = wp_slimstat_db::get_max_and_average_pages_per_visit();
1465
- if ( empty( $metrics_per_visit[ 0 ] ) ) {
1466
- $metrics_per_visit[ 0 ] = array( 'avghits' => 0, 'maxhits' => 0);
1467
- }
1468
- if ( intval( $new_visitors_rate ) > 99 ) {
1469
- $new_visitors_rate = '100';
1470
- }
1471
-
1472
- $results[ 0 ][ 'metric' ] = __( 'Visits', 'wp-slimstat' );
1473
- $results[ 0 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'visit_id', 'visit_id > 0 AND browser_type <> 1' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1474
- $results[ 0 ][ 'tooltip' ] = __( 'A visit is a session of at most 30 minutes. Returning visitors are counted multiple times if they perform multiple visits.', 'wp-slimstat' );
1475
-
1476
- $results[ 1 ][ 'metric' ] = __( 'Unique IPs', 'wp-slimstat' );
1477
- $results[ 1 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'ip', 'visit_id > 0 AND browser_type <> 1' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1478
- $results[ 1 ][ 'tooltip' ] = __( 'It includes only traffic generated by human visitors.', 'wp-slimstat' );
1479
-
1480
- $results[ 2 ][ 'metric' ] = __( 'Bounce rate', 'wp-slimstat' );
1481
- $results[ 2 ][ 'value' ] = number_format( $new_visitors_rate, 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] );
1482
- $results[ 2 ][ 'tooltip' ] = __( 'Percentage of single-page visits, i.e. visits in which the person left your site from the entrance page.', 'wp-slimstat' );
1483
-
1484
- $results[ 3 ][ 'metric' ] = __( 'Known visitors', 'wp-slimstat' );
1485
- $results[ 3 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'username' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1486
- $results[ 3 ][ 'tooltip' ] = __( 'Visitors who had previously left a comment on your blog.', 'wp-slimstat' );
1487
-
1488
- $results[ 4 ][ 'metric' ] = __( 'New visitors', 'wp-slimstat' );
1489
- $results[ 4 ][ 'value' ] = number_format( $new_visitors, 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1490
- $results[ 4 ][ 'tooltip' ] = __( 'Human users who visited your site only once.', 'wp-slimstat' );
1491
-
1492
- $results[ 5 ][ 'metric' ] = __( 'Bots', 'wp-slimstat' );
1493
- $results[ 5 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'browser_type = 1' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1494
-
1495
- $results[ 6 ][ 'metric' ] = __( 'Pageviews per visit', 'wp-slimstat' );
1496
- $results[ 6 ][ 'value' ] = number_format( $metrics_per_visit[ 0 ][ 'avghits' ], 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] );
1497
-
1498
- $results[ 7 ][ 'metric' ] = __( 'Longest visit', 'wp-slimstat' );
1499
- $results[ 7 ][ 'value' ] = number_format( $metrics_per_visit[ 0 ][ 'maxhits' ], 0, '', wp_slimstat_db::$formats[ 'thousand' ] ) . ' ' . __( 'hits', 'wp-slimstat' );
1500
-
1501
- return $results;
1502
- }
1503
-
1504
- public static function get_visits_duration() {
1505
- $total_human_visits = wp_slimstat_db::count_records( 'visit_id', 'visit_id > 0 AND browser_type <> 1' );
1506
- $results = array();
1507
-
1508
- $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', ' GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) >= 0 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 30' );
1509
- $average_time = 30 * $count_results;
1510
- $results[ 0 ][ 'metric' ] = __( '0 - 30 seconds', 'wp-slimstat' );
1511
- $results[ 0 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1512
- $results[ 0 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1513
-
1514
- $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 30 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 60' );
1515
- $average_time += 60 * $count_results;
1516
- $results[ 1 ][ 'metric' ] = __( '31 - 60 seconds', 'wp-slimstat' );
1517
- $results[ 1 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1518
- $results[ 1 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1519
-
1520
- $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 60 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 180' );
1521
- $average_time += 180 * $count_results;
1522
- $results[ 2 ][ 'metric' ] = __( '1 - 3 minutes', 'wp-slimstat' );
1523
- $results[ 2 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1524
- $results[ 2 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1525
-
1526
- $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 180 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 300' );
1527
- $average_time += 300 * $count_results;
1528
- $results[ 3 ][ 'metric' ] = __( '3 - 5 minutes', 'wp-slimstat' );
1529
- $results[ 3 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1530
- $results[ 3 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1531
-
1532
- $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 300 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 420' );
1533
- $average_time += 420 * $count_results;
1534
- $results[ 4 ][ 'metric' ] = __( '5 - 7 minutes', 'wp-slimstat' );
1535
- $results[ 4 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1536
- $results[ 4 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1537
-
1538
- $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 420 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 600' );
1539
- $average_time += 600* $count_results;
1540
- $results[ 5 ][ 'metric' ] = __( '7 - 10 minutes', 'wp-slimstat' );
1541
- $results[ 5 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1542
- $results[ 5 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1543
-
1544
- $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 600' );
1545
- $average_time += 900 * $count_results;
1546
- $results[ 6 ][ 'metric' ] = __( 'More than 10 minutes', 'wp-slimstat' );
1547
- $results[ 6 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1548
- $results[ 6 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1549
-
1550
- if ( $total_human_visits > 0 ) {
1551
- $average_time /= $total_human_visits;
1552
- $average_time = date('m:s', intval($average_time));
1553
- }
1554
- else{
1555
- $average_time = '0:00';
1556
- }
1557
-
1558
- $results[ 7 ][ 'metric' ] = __( 'Average visit duration', 'wp-slimstat' );
1559
- $results[ 7 ][ 'value' ] = $average_time;
1560
- $results[ 7 ][ 'details' ] = '';
1561
-
1562
- return $results;
1563
- }
1564
-
1565
- public static function get_traffic_sources_summary() {
1566
- $results = array();
1567
- $total_human_hits = wp_slimstat_db::count_records( 'id', 'visit_id > 0 AND browser_type <> 1' );
1568
- $new_visitors = wp_slimstat_db::count_records_having( 'ip', 'visit_id > 0', 'COUNT(visit_id) = 1' );
1569
- $new_visitors_rate = ($total_human_hits > 0)?sprintf("%01.2f", (100*$new_visitors/$total_human_hits)):0;
1570
- if (intval($new_visitors_rate) > 99) {
1571
- $new_visitors_rate = '100';
1572
- }
1573
-
1574
- $results[ 0 ][ 'metric' ] = __( 'Pageviews', 'wp-slimstat' );
1575
- $results[ 0 ][ 'value' ] = number_format( self::$pageviews, 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1576
- $results[ 0 ][ 'tooltip' ] = __( 'A request to load a single HTML file. Slimstat logs a "pageview" each time the tracking code is executed.', 'wp-slimstat' );
1577
-
1578
- $results[ 1 ][ 'metric' ] = __( 'Unique Referrers', 'wp-slimstat' );
1579
- $results[ 1 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'referer', "referer NOT LIKE '%{$_SERVER['SERVER_NAME']}%'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1580
- $results[ 1 ][ 'tooltip' ] = __( 'A referrer (or referring site) is the site that a visitor previously visited before following a link to your site.', 'wp-slimstat' );
1581
-
1582
- $results[ 2 ][ 'metric' ] = __( 'Direct Pageviews', 'wp-slimstat' );
1583
- $results[ 2 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'resource IS NULL' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1584
- $results[ 2 ][ 'tooltip' ] = __( "Visitors who visited the site by typing the URL directly into their browser. <em>Direct</em> can also refer to the visitors who clicked on the links from their bookmarks/favorites, untagged links within emails, or links from documents that don't include tracking variables.", 'wp-slimstat' );
1585
-
1586
- $results[ 3 ][ 'metric' ] = __( 'From a search result', 'wp-slimstat' );
1587
- $results[ 3 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', "searchterms IS NOT NULL AND referer IS NOT NULL AND referer NOT LIKE '%" . home_url() . "%'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1588
- $results[ 3 ][ 'tooltip' ] = __( "Visitors who came to your site via searches on Google or some other search engine.", 'wp-slimstat' );
1589
-
1590
- $results[ 4 ][ 'metric' ] = __( 'Unique Landing Pages', 'wp-slimstat' );
1591
- $results[ 4 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'resource' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1592
- $results[ 4 ][ 'tooltip' ] = __( "The first page that a user views during a session. This is also known as the <em>entrance page</em>. For example, if they search for 'Brooklyn Office Space,' and they land on your home page, it gets counted (for that visit) as a landing page.", 'wp-slimstat' );
1593
-
1594
- $results[ 5 ][ 'metric' ] = __( 'Bounce Pages', 'wp-slimstat' );
1595
- $results[ 5 ][ 'value' ] = number_format( wp_slimstat_db::count_bouncing_pages(), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1596
- $results[ 5 ][ 'tooltip' ] = __( 'Number of single page visits to your site over the selected period.', 'wp-slimstat' );
1597
-
1598
- $results[ 6 ][ 'metric' ] = __( 'New Visitors Rate', 'wp-slimstat' );
1599
- $results[ 6 ][ 'value' ] = number_format( $new_visitors_rate, 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] );
1600
- $results[ 6 ][ 'tooltip' ] = __( 'Percentage of single page visits, i.e. visits in which the person left your site from the entrance page.', 'wp-slimstat' );
1601
-
1602
- $results[ 7 ][ 'metric' ] = __( 'Currently from search engines', 'wp-slimstat' );
1603
- $results[ 7 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', "searchterms IS NOT NULL AND referer IS NOT NULL AND referer NOT LIKE '%" . home_url() . "%' AND dt > UNIX_TIMESTAMP() - 300", false ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1604
- $results[ 7 ][ 'tooltip' ] = __( 'Visitors who visited the site in the last 5 minutes coming from a search engine.', 'wp-slimstat' );
1605
-
1606
- return $results;
1607
- }
1608
-
1609
- public static function show_rankings(){
1610
- $options = array( 'timeout' => 1, 'headers' => array( 'Accept' => 'application/json' ) );
1611
- $site_url = parse_url( home_url(), PHP_URL_HOST );
1612
- if ( !empty( wp_slimstat_db::$filters_normalized[ 'resource' ] ) && wp_slimstat_db::$filters_normalized[ 'resource' ][ 0 ] == 'equals' ) {
1613
- $site_url .= wp_slimstat_db::$filters_normalized[ 'resource' ][ 1 ];
1614
- }
1615
- $site_url = urlencode( $site_url );
1616
-
1617
- // Check if we have a valied transient
1618
- if ( false === ( $rankings = get_transient( 'slimstat_ranking_values' ) ) ) {
1619
- $rankings = array(
1620
- 'seomoz_equity_backlinks' => array(
1621
- 0,
1622
- __( 'Backlinks', 'wp-slimstat' ),
1623
- __( 'Number of external equity links to your website.', 'wp-slimstat' )
1624
- ),
1625
- 'seomoz_mozrank' => array(
1626
- 0,
1627
- __( 'MozRank', 'wp-slimstat' ),
1628
- __( 'MozRank of the URL, in a normalized 10-point score. MozRank represents a link popularity score. It reflects the importance of any given web page on the Internet.', 'wp-slimstat' )
1629
- ),
1630
- 'seomoz_equity_links' => array(
1631
- 0,
1632
- __( 'Equity Links', 'wp-slimstat' ),
1633
- __( 'Number of authority-passing links (including followed links and redirects, internal or external) to your website. Set the permalink filter here above to get the corresponding metrics in this report.', 'wp-slimstat' )
1634
- ),
1635
- 'facebook_shares' => array(
1636
- 0,
1637
- __( 'Facebook Shares', 'wp-slimstat' ),
1638
- ''
1639
- ),
1640
- 'facebook_clicks' => array(
1641
- 0,
1642
- __( 'Facebook Clicks', 'wp-slimstat' ),
1643
- __( 'How many times links to your website have been clicked on Facebook.', 'wp-slimstat' )
1644
- ),
1645
- 'alexa_world_rank' => array(
1646
- 0,
1647
- __( 'Alexa World Rank', 'wp-slimstat' ),
1648
- __( 'Alexa is a subsidiary company of Amazon.com which provides commercial web traffic data.', 'wp-slimstat' )
1649
- ),
1650
- 'alexa_country_rank' => array(
1651
- 0,
1652
- __( 'Alexa Country Rank', 'wp-slimstat' ),
1653
- ''
1654
- ),
1655
- 'alexa_popularity' => array(
1656
- 0,
1657
- __( 'Alexa Popularity', 'wp-slimstat' ),
1658
- ''
1659
- )
1660
- );
1661
-
1662
- if ( !empty( wp_slimstat::$settings[ 'mozcom_access_id' ] ) && !empty( wp_slimstat::$settings[ 'mozcom_secret_key' ] ) ) {
1663
- $expiration_token = time() + 300;
1664
- $binary_signature = @hash_hmac( 'sha1', wp_slimstat::$settings[ 'mozcom_access_id' ] . "\n" . $expiration_token, wp_slimstat::$settings[ 'mozcom_secret_key' ], true );
1665
- $binary_signature = urlencode( base64_encode( $binary_signature ) );
1666
-
1667
- // SeoMoz Equity Links (Backlinks) and MozRank
1668
- $response = @wp_remote_get( 'http://lsapi.seomoz.com/linkscape/url-metrics/' . $site_url . '?Cols=16672&AccessID=' . wp_slimstat::$settings[ 'mozcom_access_id' ] . '&Expires=' . $expiration_token . '&Signature=' . $binary_signature, $options );
1669
- if ( !is_wp_error( $response ) && isset( $response[ 'response' ][ 'code' ] ) && ( $response[ 'response' ][ 'code' ] == 200 ) && !empty( $response[ 'body' ] ) ) {
1670
- $response = @json_decode( $response[ 'body' ] );
1671
- if ( is_object( $response ) ) {
1672
- if ( !empty( $response->ujid ) ) {
1673
- $rankings[ 'seomoz_equity_links' ][ 0 ] = number_format( intval( $response->ujid ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1674
- }
1675
-
1676
- if ( !empty( $response->ueid ) ) {
1677
- $rankings[ 'seomoz_equity_backlinks' ][ 0 ] = number_format( intval( $response->ueid ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1678
- }
1679
-
1680
- if ( !empty( $response->umrp ) ) {
1681
- $rankings[ 'seomoz_mozrank' ][ 0 ] = number_format( floatval( $response->umrp ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] );
1682
- }
1683
- }
1684
- }
1685
- }
1686
-
1687
- // Alexa
1688
- $response = @wp_remote_get( 'http://data.alexa.com/data?cli=10&dat=snbamz&url=' . $site_url, $options );
1689
- if ( !is_wp_error( $response ) && isset( $response[ 'response' ][ 'code' ] ) && ( $response[ 'response' ][ 'code' ] == 200 ) && !empty( $response[ 'body' ] ) ) {
1690
- $response = @simplexml_load_string( $response[ 'body' ] );
1691
- if ( is_object( $response->SD[ 1 ] ) ) {
1692
- if ( $response->SD[ 1 ]->POPULARITY && $response->SD[ 1 ]->POPULARITY->attributes() ) {
1693
- $popularity = $response->SD[ 1 ]->POPULARITY->attributes();
1694
- if ( !empty( $popularity ) ) {
1695
- $rankings[ 'alexa_popularity' ][ 0 ] = number_format( floatval( $popularity[ 'TEXT' ] ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1696
- }
1697
- }
1698
-
1699
- if ( $response->SD[ 1 ]->REACH && $response->SD[ 1 ]->REACH->attributes() ) {
1700
- $reach = $response->SD[ 1 ]->REACH->attributes();
1701
- if ( !empty( $reach ) ) {
1702
- $rankings[ 'alexa_world_rank' ][ 0 ] = number_format( floatval( $reach[ 'RANK' ] ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1703
- }
1704
- }
1705
-
1706
- if ( $response->SD[ 1 ]->COUNTRY && $response->SD[ 1 ]->COUNTRY->attributes() ) {
1707
- $country = $response->SD[ 1 ]->COUNTRY->attributes();
1708
- if ( !empty( $country ) ) {
1709
- $rankings[ 'alexa_country_rank' ][ 0 ] = number_format( floatval( $country[ 'RANK' ] ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1710
- }
1711
- }
1712
- else if ( $response->SD[ 1 ]->RANK && $response->SD[ 1 ]->RANK->attributes() ) {
1713
- $rank = $response->SD[ 1 ]->RANK->attributes();
1714
- if ( !empty( $rank ) ) {
1715
- $rankings[ 'alexa_country_rank' ][ 0 ] = number_format( floatval( $rank[ 'DELTA' ] ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1716
- $rankings[ 'alexa_country_rank' ][ 1 ] = __( 'Alexa Delta', 'wp-slimstat' );
1717
- }
1718
- }
1719
- }
1720
- }
1721
-
1722
- // Facebook
1723
- $options[ 'headers' ][ 'Accept' ] = 'text/xml';
1724
- $response = @wp_remote_get( 'http://api.facebook.com/restserver.php?method=links.getStats&urls=' . $site_url, $options );
1725
- if ( !is_wp_error( $response ) && isset( $response[ 'response' ][ 'code' ] ) && ( $response[ 'response' ][ 'code' ] == 200 ) && !empty( $response[ 'body' ] ) ) {
1726
- $response = @simplexml_load_string( $response[ 'body' ] );
1727
- if ( is_object( $response ) && is_object( $response->link_stat ) ) {
1728
- $rankings[ 'facebook_shares' ][ 0 ] = number_format( intval( $response->link_stat->share_count ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1729
- $rankings[ 'facebook_clicks' ][ 0 ] = number_format( intval( $response->link_stat->click_count ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1730
- }
1731
- }
1732
-
1733
- // Store rankings as transients for 12 hours
1734
- //set_transient('slimstat_ranking_values', $rankings, 43200);
1735
- }
1736
-
1737
- foreach ( $rankings as $a_ranking ) {
1738
- echo '<p>' . self::inline_help( $a_ranking[ 2 ], false ) . $a_ranking[ 1 ] . '<span>' . $a_ranking[ 0 ] . '</span></p>';
1739
- }
1740
-
1741
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1742
- die();
1743
- }
1744
- }
1745
-
1746
- public static function get_your_blog(){
1747
- if ( false === ( $results = get_transient( 'slimstat_your_content' ) ) ) {
1748
- $results = array();
1749
-
1750
- $results[ 0 ][ 'metric' ] = __( 'Content Items', 'wp-slimstat' );
1751
- $results[ 0 ][ 'value' ] = number_format( $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->posts} WHERE post_type <> 'revision' AND post_status <> 'auto-draft'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1752
- $results[ 0 ][ 'tooltip' ] = __( 'This value includes not only posts, but also custom post types, regardless of their status', 'wp-slimstat' );
1753
-
1754
- $results[ 1 ][ 'metric' ] = __( 'Posts', 'wp-slimstat' );
1755
- $results[ 1 ][ 'value' ] = $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->posts} WHERE post_type = 'post'" );
1756
-
1757
- $results[ 2 ][ 'metric' ] = __( 'Pages', 'wp-slimstat' );
1758
- $results[ 2 ][ 'value' ] = number_format( $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->posts} WHERE post_type = 'page'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1759
-
1760
- $results[ 3 ][ 'metric' ] = __( 'Attachments', 'wp-slimstat' );
1761
- $results[ 3 ][ 'value' ] = number_format( $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->posts} WHERE post_type = 'attachment'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1762
-
1763
- $results[ 4 ][ 'metric' ] = __( 'Revisions', 'wp-slimstat' );
1764
- $results[ 4 ][ 'value' ] = number_format( $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->posts} WHERE post_type = 'revision'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1765
-
1766
- $results[ 5 ][ 'metric' ] = __( 'Comments', 'wp-slimstat' );
1767
- $results[ 5 ][ 'value' ] = $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->comments}" );
1768
-
1769
- $results[ 6 ][ 'metric' ] = __( 'Avg Comments per Post', 'wp-slimstat' );
1770
- $results[ 6 ][ 'value' ] = number_format( !empty( $results[ 1 ][ 'value' ] ) ? $results[ 5 ][ 'value' ] / $results[ 1 ][ 'value' ] : 0, 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1771
-
1772
- $results[ 7 ][ 'metric' ] = __( 'Avg Server Latency', 'wp-slimstat' );
1773
- $results[ 7 ][ 'value' ] = number_format( wp_slimstat::$wpdb->get_var( "SELECT AVG(server_latency) FROM {$GLOBALS[ 'wpdb' ]->prefix }slim_stats WHERE server_latency <> 0" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1774
- $results[ 7 ][ 'tooltip' ] = __( 'Latency is the amount of time it takes for the host server to receive and process a request for a page object. The amount of latency depends largely on how far away the user is from the server.', 'wp-slimstat' );
1775
-
1776
- $results[ 1 ][ 'value' ] = number_format( $results[ 1 ][ 'value' ], 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1777
- $results[ 5 ][ 'value' ] = number_format( $results[ 5 ][ 'value' ], 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1778
-
1779
- // Store values as transients for 30 minutes
1780
- set_transient( 'slimstat_your_content', $results, 1800 );
1781
- }
1782
-
1783
- return $results;
1784
- }
1785
-
1786
- public static function show_events( $_args = array() ) {
1787
- $all_results = call_user_func( $_args[ 'raw' ] , $_args );
1788
-
1789
- $results = array_slice(
1790
- $all_results,
1791
- wp_slimstat_db::$filters_normalized[ 'misc' ][ 'start_from' ],
1792
- wp_slimstat::$settings[ 'rows_to_show' ]
1793
- );
1794
-
1795
- // Count the results
1796
- $count_page_results = count( $results );
1797
-
1798
- if ($count_page_results == 0){
1799
- echo '<p class="nodata">' . __( 'No data to display', 'wp-slimstat' ) . '</p>';
1800
-
1801
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1802
- die();
1803
- }
1804
- else{
1805
- return array();
1806
- }
1807
- }
1808
-
1809
- echo self::report_pagination( $count_page_results, count( $all_results ) );
1810
- $is_expanded = ( wp_slimstat::$settings[ 'expand_details' ] == 'on' ) ? ' expanded' : '';
1811
-
1812
- foreach ( $results as $a_result ) {
1813
- echo "<p class='slimstat-tooltip-trigger'>{$a_result[ 'notes' ]} <b class='slimstat-tooltip-content$is_expanded'>" . __( 'Type', 'wp-slimstat' ) . ": {$a_result[ 'type' ]}";
1814
-
1815
- if ( !empty( $a_result[ 'dt' ] ) ) {
1816
- $date_time = date_i18n( wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ], $a_result[ 'dt' ], true );
1817
- echo '<br/>' . __( 'Coordinates', 'wp-slimstat' ) . ": {$a_result[ 'position' ]}<br/>" . __( 'Date', 'wp-slimstat' ) . ": $date_time";
1818
- }
1819
- if ( !empty( $a_result[ 'counthits' ] ) ) {
1820
- echo '<br/>' . __( 'Hits', 'wp-slimstat' ) . ": {$a_result[ 'counthits' ]}";
1821
- }
1822
-
1823
- echo "</b></p>";
1824
- }
1825
-
1826
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1827
- die();
1828
- }
1829
- }
1830
-
1831
- public static function show_world_map() {
1832
- $countries = wp_slimstat_db::get_top( 'country' );
1833
- $recent_visits = wp_slimstat_db::get_recent( 'location', '', '', true, '', 'city' );
1834
-
1835
- $data_points = array();
1836
- if ( !empty( $recent_visits ) ) {
1837
- $recent_visits = array_slice( $recent_visits, 0, wp_slimstat::$settings[ 'max_dots_on_map' ] );
1838
-
1839
- foreach ( $recent_visits as $a_recent_visit ) {
1840
- if ( !empty( $a_recent_visit[ 'city' ] ) && !empty( $a_recent_visit[ 'location' ] ) ) {
1841
- list( $latitude, $longitude ) = explode( ',', $a_recent_visit[ 'location' ] );
1842
- $clean_city_name = htmlentities( $a_recent_visit[ 'city' ], ENT_QUOTES, 'UTF-8' );
1843
- $date_time = date_i18n( wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ], $a_recent_visit[ 'dt' ], true );
1844
- $data_points[] = "{zoomLevel:7,type:'circle',title:'{$clean_city_name}<br>{$date_time}',latitude:$latitude,longitude:$longitude}";
1845
- }
1846
- }
1847
- }
1848
-
1849
- $data_areas = array('xx'=>'{id:"XX",balloonText:"'.__('c-xx','wp-slimstat').': 0",value:0,color:"#ededed"}','af'=>'{id:"AF",balloonText:"'.__('c-af','wp-slimstat').': 0",value:0,color:"#ededed"}','ax'=>'{id:"AX",balloonText:"'.__('c-ax','wp-slimstat').': 0",value:0,color:"#ededed"}','al'=>'{id:"AL",balloonText:"'.__('c-al','wp-slimstat').': 0",value:0,color:"#ededed"}','dz'=>'{id:"DZ",balloonText:"'.__('c-dz','wp-slimstat').': 0",value:0,color:"#ededed"}','ad'=>'{id:"AD",balloonText:"'.__('c-ad','wp-slimstat').': 0",value:0,color:"#ededed"}','ao'=>'{id:"AO",balloonText:"'.__('c-ao','wp-slimstat').': 0",value:0,color:"#ededed"}','ai'=>'{id:"AI",balloonText:"'.__('c-ai','wp-slimstat').': 0",value:0,color:"#ededed"}','ag'=>'{id:"AG",balloonText:"'.__('c-ag','wp-slimstat').': 0",value:0,color:"#ededed"}','ar'=>'{id:"AR",balloonText:"'.__('c-ar','wp-slimstat').': 0",value:0,color:"#ededed"}','am'=>'{id:"AM",balloonText:"'.__('c-am','wp-slimstat').': 0",value:0,color:"#ededed"}','aw'=>'{id:"AW",balloonText:"'.__('c-aw','wp-slimstat').': 0",value:0,color:"#ededed"}','au'=>'{id:"AU",balloonText:"'.__('c-au','wp-slimstat').': 0",value:0,color:"#ededed"}','at'=>'{id:"AT",balloonText:"'.__('c-at','wp-slimstat').': 0",value:0,color:"#ededed"}','az'=>'{id:"AZ",balloonText:"'.__('c-az','wp-slimstat').': 0",value:0,color:"#ededed"}','bs'=>'{id:"BS",balloonText:"'.__('c-bs','wp-slimstat').': 0",value:0,color:"#ededed"}','bh'=>'{id:"BH",balloonText:"'.__('c-bh','wp-slimstat').': 0",value:0,color:"#ededed"}','bd'=>'{id:"BD",balloonText:"'.__('c-bd','wp-slimstat').': 0",value:0,color:"#ededed"}','bb'=>'{id:"BB",balloonText:"'.__('c-bb','wp-slimstat').': 0",value:0,color:"#ededed"}','by'=>'{id:"BY",balloonText:"'.__('c-by','wp-slimstat').': 0",value:0,color:"#ededed"}','be'=>'{id:"BE",balloonText:"'.__('c-be','wp-slimstat').': 0",value:0,color:"#ededed"}','bz'=>'{id:"BZ",balloonText:"'.__('c-bz','wp-slimstat').': 0",value:0,color:"#ededed"}','bj'=>'{id:"BJ",balloonText:"'.__('c-bj','wp-slimstat').': 0",value:0,color:"#ededed"}','bm'=>'{id:"BM",balloonText:"'.__('c-bm','wp-slimstat').': 0",value:0,color:"#ededed"}','bt'=>'{id:"BT",balloonText:"'.__('c-bt','wp-slimstat').': 0",value:0,color:"#ededed"}','bo'=>'{id:"BO",balloonText:"'.__('c-bo','wp-slimstat').': 0",value:0,color:"#ededed"}','ba'=>'{id:"BA",balloonText:"'.__('c-ba','wp-slimstat').': 0",value:0,color:"#ededed"}','bw'=>'{id:"BW",balloonText:"'.__('c-bw','wp-slimstat').': 0",value:0,color:"#ededed"}','br'=>'{id:"BR",balloonText:"'.__('c-br','wp-slimstat').': 0",value:0,color:"#ededed"}','bn'=>'{id:"BN",balloonText:"'.__('c-bn','wp-slimstat').': 0",value:0,color:"#ededed"}','bg'=>'{id:"BG",balloonText:"'.__('c-bg','wp-slimstat').': 0",value:0,color:"#ededed"}','bf'=>'{id:"BF",balloonText:"'.__('c-bf','wp-slimstat').': 0",value:0,color:"#ededed"}','bi'=>'{id:"BI",balloonText:"'.__('c-bi','wp-slimstat').': 0",value:0,color:"#ededed"}','kh'=>'{id:"KH",balloonText:"'.__('c-kh','wp-slimstat').': 0",value:0,color:"#ededed"}','cm'=>'{id:"CM",balloonText:"'.__('c-cm','wp-slimstat').': 0",value:0,color:"#ededed"}','ca'=>'{id:"CA",balloonText:"'.__('c-ca','wp-slimstat').': 0",value:0,color:"#ededed"}','cv'=>'{id:"CV",balloonText:"'.__('c-cv','wp-slimstat').': 0",value:0,color:"#ededed"}','ky'=>'{id:"KY",balloonText:"'.__('c-ky','wp-slimstat').': 0",value:0,color:"#ededed"}','cf'=>'{id:"CF",balloonText:"'.__('c-cf','wp-slimstat').': 0",value:0,color:"#ededed"}','td'=>'{id:"TD",balloonText:"'.__('c-td','wp-slimstat').': 0",value:0,color:"#ededed"}','cl'=>'{id:"CL",balloonText:"'.__('c-cl','wp-slimstat').': 0",value:0,color:"#ededed"}','cn'=>'{id:"CN",balloonText:"'.__('c-cn','wp-slimstat').': 0",value:0,color:"#ededed"}','co'=>'{id:"CO",balloonText:"'.__('c-co','wp-slimstat').': 0",value:0,color:"#ededed"}','km'=>'{id:"KM",balloonText:"'.__('c-km','wp-slimstat').': 0",value:0,color:"#ededed"}','cg'=>'{id:"CG",balloonText:"'.__('c-cg','wp-slimstat').': 0",value:0,color:"#ededed"}','cd'=>'{id:"CD",balloonText:"'.__('c-cd','wp-slimstat').': 0",value:0,color:"#ededed"}','cr'=>'{id:"CR",balloonText:"'.__('c-cr','wp-slimstat').': 0",value:0,color:"#ededed"}','ci'=>'{id:"CI",balloonText:"'.__('c-ci','wp-slimstat').': 0",value:0,color:"#ededed"}','hr'=>'{id:"HR",balloonText:"'.__('c-hr','wp-slimstat').': 0",value:0,color:"#ededed"}','cu'=>'{id:"CU",balloonText:"'.__('c-cu','wp-slimstat').': 0",value:0,color:"#ededed"}','cy'=>'{id:"CY",balloonText:"'.__('c-cy','wp-slimstat').': 0",value:0,color:"#ededed"}','cz'=>'{id:"CZ",balloonText:"'.__('c-cz','wp-slimstat').': 0",value:0,color:"#ededed"}','dk'=>'{id:"DK",balloonText:"'.__('c-dk','wp-slimstat').': 0",value:0,color:"#ededed"}','dj'=>'{id:"DJ",balloonText:"'.__('c-dj','wp-slimstat').': 0",value:0,color:"#ededed"}','dm'=>'{id:"DM",balloonText:"'.__('c-dm','wp-slimstat').': 0",value:0,color:"#ededed"}','do'=>'{id:"DO",balloonText:"'.__('c-do','wp-slimstat').': 0",value:0,color:"#ededed"}','ec'=>'{id:"EC",balloonText:"'.__('c-ec','wp-slimstat').': 0",value:0,color:"#ededed"}','eg'=>'{id:"EG",balloonText:"'.__('c-eg','wp-slimstat').': 0",value:0,color:"#ededed"}','sv'=>'{id:"SV",balloonText:"'.__('c-sv','wp-slimstat').': 0",value:0,color:"#ededed"}','gq'=>'{id:"GQ",balloonText:"'.__('c-gq','wp-slimstat').': 0",value:0,color:"#ededed"}','er'=>'{id:"ER",balloonText:"'.__('c-er','wp-slimstat').': 0",value:0,color:"#ededed"}','ee'=>'{id:"EE",balloonText:"'.__('c-ee','wp-slimstat').': 0",value:0,color:"#ededed"}','et'=>'{id:"ET",balloonText:"'.__('c-et','wp-slimstat').': 0",value:0,color:"#ededed"}','fo'=>'{id:"FO",balloonText:"'.__('c-fo','wp-slimstat').': 0",value:0,color:"#ededed"}','fk'=>'{id:"FK",balloonText:"'.__('c-fk','wp-slimstat').': 0",value:0,color:"#ededed"}','fj'=>'{id:"FJ",balloonText:"'.__('c-fj','wp-slimstat').': 0",value:0,color:"#ededed"}','fi'=>'{id:"FI",balloonText:"'.__('c-fi','wp-slimstat').': 0",value:0,color:"#ededed"}','fr'=>'{id:"FR",balloonText:"'.__('c-fr','wp-slimstat').': 0",value:0,color:"#ededed"}','gf'=>'{id:"GF",balloonText:"'.__('c-gf','wp-slimstat').': 0",value:0,color:"#ededed"}','ga'=>'{id:"GA",balloonText:"'.__('c-ga','wp-slimstat').': 0",value:0,color:"#ededed"}','gm'=>'{id:"GM",balloonText:"'.__('c-gm','wp-slimstat').': 0",value:0,color:"#ededed"}','ge'=>'{id:"GE",balloonText:"'.__('c-ge','wp-slimstat').': 0",value:0,color:"#ededed"}','de'=>'{id:"DE",balloonText:"'.__('c-de','wp-slimstat').': 0",value:0,color:"#ededed"}','gh'=>'{id:"GH",balloonText:"'.__('c-gh','wp-slimstat').': 0",value:0,color:"#ededed"}','gr'=>'{id:"GR",balloonText:"'.__('c-gr','wp-slimstat').': 0",value:0,color:"#ededed"}','gl'=>'{id:"GL",balloonText:"'.__('c-gl','wp-slimstat').': 0",value:0,color:"#ededed"}','gd'=>'{id:"GD",balloonText:"'.__('c-gd','wp-slimstat').': 0",value:0,color:"#ededed"}','gp'=>'{id:"GP",balloonText:"'.__('c-gp','wp-slimstat').': 0",value:0,color:"#ededed"}','gt'=>'{id:"GT",balloonText:"'.__('c-gt','wp-slimstat').': 0",value:0,color:"#ededed"}','gn'=>'{id:"GN",balloonText:"'.__('c-gn','wp-slimstat').': 0",value:0,color:"#ededed"}','gw'=>'{id:"GW",balloonText:"'.__('c-gw','wp-slimstat').': 0",value:0,color:"#ededed"}','gy'=>'{id:"GY",balloonText:"'.__('c-gy','wp-slimstat').': 0",value:0,color:"#ededed"}','ht'=>'{id:"HT",balloonText:"'.__('c-ht','wp-slimstat').': 0",value:0,color:"#ededed"}','hn'=>'{id:"HN",balloonText:"'.__('c-hn','wp-slimstat').': 0",value:0,color:"#ededed"}','hk'=>'{id:"HK",balloonText:"'.__('c-hk','wp-slimstat').': 0",value:0,color:"#ededed"}','hu'=>'{id:"HU",balloonText:"'.__('c-hu','wp-slimstat').': 0",value:0,color:"#ededed"}','is'=>'{id:"IS",balloonText:"'.__('c-is','wp-slimstat').': 0",value:0,color:"#ededed"}','in'=>'{id:"IN",balloonText:"'.__('c-in','wp-slimstat').': 0",value:0,color:"#ededed"}','id'=>'{id:"ID",balloonText:"'.__('c-id','wp-slimstat').': 0",value:0,color:"#ededed"}','ir'=>'{id:"IR",balloonText:"'.__('c-ir','wp-slimstat').': 0",value:0,color:"#ededed"}','iq'=>'{id:"IQ",balloonText:"'.__('c-iq','wp-slimstat').': 0",value:0,color:"#ededed"}','ie'=>'{id:"IE",balloonText:"'.__('c-ie','wp-slimstat').': 0",value:0,color:"#ededed"}','il'=>'{id:"IL",balloonText:"'.__('c-il','wp-slimstat').': 0",value:0,color:"#ededed"}','it'=>'{id:"IT",balloonText:"'.__('c-it','wp-slimstat').': 0",value:0,color:"#ededed"}','jm'=>'{id:"JM",balloonText:"'.__('c-jm','wp-slimstat').': 0",value:0,color:"#ededed"}','jp'=>'{id:"JP",balloonText:"'.__('c-jp','wp-slimstat').': 0",value:0,color:"#ededed"}','jo'=>'{id:"JO",balloonText:"'.__('c-jo','wp-slimstat').': 0",value:0,color:"#ededed"}','kz'=>'{id:"KZ",balloonText:"'.__('c-kz','wp-slimstat').': 0",value:0,color:"#ededed"}','ke'=>'{id:"KE",balloonText:"'.__('c-ke','wp-slimstat').': 0",value:0,color:"#ededed"}','nr'=>'{id:"NR",balloonText:"'.__('c-nr','wp-slimstat').': 0",value:0,color:"#ededed"}','kp'=>'{id:"KP",balloonText:"'.__('c-kp','wp-slimstat').': 0",value:0,color:"#ededed"}','kr'=>'{id:"KR",balloonText:"'.__('c-kr','wp-slimstat').': 0",value:0,color:"#ededed"}','kv'=>'{id:"KV",balloonText:"'.__('c-kv','wp-slimstat').': 0",value:0,color:"#ededed"}','kw'=>'{id:"KW",balloonText:"'.__('c-kw','wp-slimstat').': 0",value:0,color:"#ededed"}','kg'=>'{id:"KG",balloonText:"'.__('c-kg','wp-slimstat').': 0",value:0,color:"#ededed"}','la'=>'{id:"LA",balloonText:"'.__('c-la','wp-slimstat').': 0",value:0,color:"#ededed"}','lv'=>'{id:"LV",balloonText:"'.__('c-lv','wp-slimstat').': 0",value:0,color:"#ededed"}','lb'=>'{id:"LB",balloonText:"'.__('c-lb','wp-slimstat').': 0",value:0,color:"#ededed"}','ls'=>'{id:"LS",balloonText:"'.__('c-ls','wp-slimstat').': 0",value:0,color:"#ededed"}','lr'=>'{id:"LR",balloonText:"'.__('c-lr','wp-slimstat').': 0",value:0,color:"#ededed"}','ly'=>'{id:"LY",balloonText:"'.__('c-ly','wp-slimstat').': 0",value:0,color:"#ededed"}','li'=>'{id:"LI",balloonText:"'.__('c-li','wp-slimstat').': 0",value:0,color:"#ededed"}','lt'=>'{id:"LT",balloonText:"'.__('c-lt','wp-slimstat').': 0",value:0,color:"#ededed"}','lu'=>'{id:"LU",balloonText:"'.__('c-lu','wp-slimstat').': 0",value:0,color:"#ededed"}','mk'=>'{id:"MK",balloonText:"'.__('c-mk','wp-slimstat').': 0",value:0,color:"#ededed"}','mg'=>'{id:"MG",balloonText:"'.__('c-mg','wp-slimstat').': 0",value:0,color:"#ededed"}','mw'=>'{id:"MW",balloonText:"'.__('c-mw','wp-slimstat').': 0",value:0,color:"#ededed"}','my'=>'{id:"MY",balloonText:"'.__('c-my','wp-slimstat').': 0",value:0,color:"#ededed"}','ml'=>'{id:"ML",balloonText:"'.__('c-ml','wp-slimstat').': 0",value:0,color:"#ededed"}','mt'=>'{id:"MT",balloonText:"'.__('c-mt','wp-slimstat').': 0",value:0,color:"#ededed"}','mq'=>'{id:"MQ",balloonText:"'.__('c-mq','wp-slimstat').': 0",value:0,color:"#ededed"}','mr'=>'{id:"MR",balloonText:"'.__('c-mr','wp-slimstat').': 0",value:0,color:"#ededed"}','mu'=>'{id:"MU",balloonText:"'.__('c-mu','wp-slimstat').': 0",value:0,color:"#ededed"}','mx'=>'{id:"MX",balloonText:"'.__('c-mx','wp-slimstat').': 0",value:0,color:"#ededed"}','md'=>'{id:"MD",balloonText:"'.__('c-md','wp-slimstat').': 0",value:0,color:"#ededed"}','mn'=>'{id:"MN",balloonText:"'.__('c-mn','wp-slimstat').': 0",value:0,color:"#ededed"}','me'=>'{id:"ME",balloonText:"'.__('c-me','wp-slimstat').': 0",value:0,color:"#ededed"}','ms'=>'{id:"MS",balloonText:"'.__('c-ms','wp-slimstat').': 0",value:0,color:"#ededed"}','ma'=>'{id:"MA",balloonText:"'.__('c-ma','wp-slimstat').': 0",value:0,color:"#ededed"}','mz'=>'{id:"MZ",balloonText:"'.__('c-mz','wp-slimstat').': 0",value:0,color:"#ededed"}','mm'=>'{id:"MM",balloonText:"'.__('c-mm','wp-slimstat').': 0",value:0,color:"#ededed"}','na'=>'{id:"NA",balloonText:"'.__('c-na','wp-slimstat').': 0",value:0,color:"#ededed"}','np'=>'{id:"NP",balloonText:"'.__('c-np','wp-slimstat').': 0",value:0,color:"#ededed"}','nl'=>'{id:"NL",balloonText:"'.__('c-nl','wp-slimstat').': 0",value:0,color:"#ededed"}','nc'=>'{id:"NC",balloonText:"'.__('c-nc','wp-slimstat').': 0",value:0,color:"#ededed"}','nz'=>'{id:"NZ",balloonText:"'.__('c-nz','wp-slimstat').': 0",value:0,color:"#ededed"}','ni'=>'{id:"NI",balloonText:"'.__('c-ni','wp-slimstat').': 0",value:0,color:"#ededed"}','ne'=>'{id:"NE",balloonText:"'.__('c-ne','wp-slimstat').': 0",value:0,color:"#ededed"}','ng'=>'{id:"NG",balloonText:"'.__('c-ng','wp-slimstat').': 0",value:0,color:"#ededed"}','no'=>'{id:"NO",balloonText:"'.__('c-no','wp-slimstat').': 0",value:0,color:"#ededed"}','om'=>'{id:"OM",balloonText:"'.__('c-om','wp-slimstat').': 0",value:0,color:"#ededed"}','pk'=>'{id:"PK",balloonText:"'.__('c-pk','wp-slimstat').': 0",value:0,color:"#ededed"}','pw'=>'{id:"PW",balloonText:"'.__('c-pw','wp-slimstat').': 0",value:0,color:"#ededed"}','ps'=>'{id:"PS",balloonText:"'.__('c-ps','wp-slimstat').': 0",value:0,color:"#ededed"}','pa'=>'{id:"PA",balloonText:"'.__('c-pa','wp-slimstat').': 0",value:0,color:"#ededed"}','pg'=>'{id:"PG",balloonText:"'.__('c-pg','wp-slimstat').': 0",value:0,color:"#ededed"}','py'=>'{id:"PY",balloonText:"'.__('c-py','wp-slimstat').': 0",value:0,color:"#ededed"}','pe'=>'{id:"PE",balloonText:"'.__('c-pe','wp-slimstat').': 0",value:0,color:"#ededed"}','ph'=>'{id:"PH",balloonText:"'.__('c-ph','wp-slimstat').': 0",value:0,color:"#ededed"}','pl'=>'{id:"PL",balloonText:"'.__('c-pl','wp-slimstat').': 0",value:0,color:"#ededed"}','pt'=>'{id:"PT",balloonText:"'.__('c-pt','wp-slimstat').': 0",value:0,color:"#ededed"}','pr'=>'{id:"PR",balloonText:"'.__('c-pr','wp-slimstat').': 0",value:0,color:"#ededed"}','qa'=>'{id:"QA",balloonText:"'.__('c-qa','wp-slimstat').': 0",value:0,color:"#ededed"}','re'=>'{id:"RE",balloonText:"'.__('c-re','wp-slimstat').': 0",value:0,color:"#ededed"}','ro'=>'{id:"RO",balloonText:"'.__('c-ro','wp-slimstat').': 0",value:0,color:"#ededed"}','ru'=>'{id:"RU",balloonText:"'.__('c-ru','wp-slimstat').': 0",value:0,color:"#ededed"}','rw'=>'{id:"RW",balloonText:"'.__('c-rw','wp-slimstat').': 0",value:0,color:"#ededed"}','kn'=>'{id:"KN",balloonText:"'.__('c-kn','wp-slimstat').': 0",value:0,color:"#ededed"}','lc'=>'{id:"LC",balloonText:"'.__('c-lc','wp-slimstat').': 0",value:0,color:"#ededed"}','mf'=>'{id:"MF",balloonText:"'.__('c-mf','wp-slimstat').': 0",value:0,color:"#ededed"}','vc'=>'{id:"VC",balloonText:"'.__('c-vc','wp-slimstat').': 0",value:0,color:"#ededed"}','ws'=>'{id:"WS",balloonText:"'.__('c-ws','wp-slimstat').': 0",value:0,color:"#ededed"}','st'=>'{id:"ST",balloonText:"'.__('c-st','wp-slimstat').': 0",value:0,color:"#ededed"}','sa'=>'{id:"SA",balloonText:"'.__('c-sa','wp-slimstat').': 0",value:0,color:"#ededed"}','sn'=>'{id:"SN",balloonText:"'.__('c-sn','wp-slimstat').': 0",value:0,color:"#ededed"}','rs'=>'{id:"RS",balloonText:"'.__('c-rs','wp-slimstat').': 0",value:0,color:"#ededed"}','sl'=>'{id:"SL",balloonText:"'.__('c-sl','wp-slimstat').': 0",value:0,color:"#ededed"}','sg'=>'{id:"SG",balloonText:"'.__('c-sg','wp-slimstat').': 0",value:0,color:"#ededed"}','sk'=>'{id:"SK",balloonText:"'.__('c-sk','wp-slimstat').': 0",value:0,color:"#ededed"}','si'=>'{id:"SI",balloonText:"'.__('c-si','wp-slimstat').': 0",value:0,color:"#ededed"}','sb'=>'{id:"SB",balloonText:"'.__('c-sb','wp-slimstat').': 0",value:0,color:"#ededed"}','so'=>'{id:"SO",balloonText:"'.__('c-so','wp-slimstat').': 0",value:0,color:"#ededed"}','za'=>'{id:"ZA",balloonText:"'.__('c-za','wp-slimstat').': 0",value:0,color:"#ededed"}','gs'=>'{id:"GS",balloonText:"'.__('c-gs','wp-slimstat').': 0",value:0,color:"#ededed"}','es'=>'{id:"ES",balloonText:"'.__('c-es','wp-slimstat').': 0",value:0,color:"#ededed"}','lk'=>'{id:"LK",balloonText:"'.__('c-lk','wp-slimstat').': 0",value:0,color:"#ededed"}','sc'=>'{id:"SC",balloonText:"'.__('c-sc','wp-slimstat').': 0",value:0,color:"#ededed"}','sd'=>'{id:"SD",balloonText:"'.__('c-sd','wp-slimstat').': 0",value:0,color:"#ededed"}','ss'=>'{id:"SS",balloonText:"'.__('c-ss','wp-slimstat').': 0",value:0,color:"#ededed"}','sr'=>'{id:"SR",balloonText:"'.__('c-sr','wp-slimstat').': 0",value:0,color:"#ededed"}','sj'=>'{id:"SJ",balloonText:"'.__('c-sj','wp-slimstat').': 0",value:0,color:"#ededed"}','sz'=>'{id:"SZ",balloonText:"'.__('c-sz','wp-slimstat').': 0",value:0,color:"#ededed"}','se'=>'{id:"SE",balloonText:"'.__('c-se','wp-slimstat').': 0",value:0,color:"#ededed"}','ch'=>'{id:"CH",balloonText:"'.__('c-ch','wp-slimstat').': 0",value:0,color:"#ededed"}','sy'=>'{id:"SY",balloonText:"'.__('c-sy','wp-slimstat').': 0",value:0,color:"#ededed"}','tw'=>'{id:"TW",balloonText:"'.__('c-tw','wp-slimstat').': 0",value:0,color:"#ededed"}','tj'=>'{id:"TJ",balloonText:"'.__('c-tj','wp-slimstat').': 0",value:0,color:"#ededed"}','tz'=>'{id:"TZ",balloonText:"'.__('c-tz','wp-slimstat').': 0",value:0,color:"#ededed"}','th'=>'{id:"TH",balloonText:"'.__('c-th','wp-slimstat').': 0",value:0,color:"#ededed"}','tl'=>'{id:"TL",balloonText:"'.__('c-tl','wp-slimstat').': 0",value:0,color:"#ededed"}','tg'=>'{id:"TG",balloonText:"'.__('c-tg','wp-slimstat').': 0",value:0,color:"#ededed"}','to'=>'{id:"TO",balloonText:"'.__('c-to','wp-slimstat').': 0",value:0,color:"#ededed"}','tt'=>'{id:"TT",balloonText:"'.__('c-tt','wp-slimstat').': 0",value:0,color:"#ededed"}','tn'=>'{id:"TN",balloonText:"'.__('c-tn','wp-slimstat').': 0",value:0,color:"#ededed"}','tr'=>'{id:"TR",balloonText:"'.__('c-tr','wp-slimstat').': 0",value:0,color:"#ededed"}','tm'=>'{id:"TM",balloonText:"'.__('c-tm','wp-slimstat').': 0",value:0,color:"#ededed"}','tc'=>'{id:"TC",balloonText:"'.__('c-tc','wp-slimstat').': 0",value:0,color:"#ededed"}','ug'=>'{id:"UG",balloonText:"'.__('c-ug','wp-slimstat').': 0",value:0,color:"#ededed"}','ua'=>'{id:"UA",balloonText:"'.__('c-ua','wp-slimstat').': 0",value:0,color:"#ededed"}','ae'=>'{id:"AE",balloonText:"'.__('c-ae','wp-slimstat').': 0",value:0,color:"#ededed"}','gb'=>'{id:"GB",balloonText:"'.__('c-gb','wp-slimstat').': 0",value:0,color:"#ededed"}','us'=>'{id:"US",balloonText:"'.__('c-us','wp-slimstat').': 0",value:0,color:"#ededed"}','uy'=>'{id:"UY",balloonText:"'.__('c-uy','wp-slimstat').': 0",value:0,color:"#ededed"}','uz'=>'{id:"UZ",balloonText:"'.__('c-uz','wp-slimstat').': 0",value:0,color:"#ededed"}','vu'=>'{id:"VU",balloonText:"'.__('c-vu','wp-slimstat').': 0",value:0,color:"#ededed"}','ve'=>'{id:"VE",balloonText:"'.__('c-ve','wp-slimstat').': 0",value:0,color:"#ededed"}','vn'=>'{id:"VN",balloonText:"'.__('c-vn','wp-slimstat').': 0",value:0,color:"#ededed"}','vg'=>'{id:"VG",balloonText:"'.__('c-vg','wp-slimstat').': 0",value:0,color:"#ededed"}','vi'=>'{id:"VI",balloonText:"'.__('c-vi','wp-slimstat').': 0",value:0,color:"#ededed"}','eh'=>'{id:"EH",balloonText:"'.__('c-eh','wp-slimstat').': 0",value:0,color:"#ededed"}','ye'=>'{id:"YE",balloonText:"'.__('c-ye','wp-slimstat').': 0",value:0,color:"#ededed"}','zm'=>'{id:"ZM",balloonText:"'.__('c-zm','wp-slimstat').': 0",value:0,color:"#ededed"}','zw'=>'{id:"ZW",balloonText:"'.__('c-zw','wp-slimstat').': 0",value:0,color:"#ededed"}','gg'=>'{id:"GG",balloonText:"'.__('c-gg','wp-slimstat').': 0",value:0,color:"#ededed"}','je'=>'{id:"JE",balloonText:"'.__('c-je','wp-slimstat').': 0",value:0,color:"#ededed"}','im'=>'{id:"IM",balloonText:"'.__('c-im','wp-slimstat').': 0",value:0,color:"#ededed"}','mv'=>'{id:"MV",balloonText:"'.__('c-mv','wp-slimstat').': 0",value:0,color:"#ededed"}');
1850
- $countries_not_represented = array( __( 'c-eu', 'wp-slimstat' ) );
1851
- $max = 0;
1852
-
1853
- foreach ( $countries as $a_country ) {
1854
- if ( !array_key_exists( $a_country[ 'country' ], $data_areas ) ) {
1855
- continue;
1856
- }
1857
-
1858
- $percentage = ( self::$pageviews > 0 ) ? sprintf( "%01.2f", ( 100 * $a_country[ 'counthits' ] / self::$pageviews ) ) : 0;
1859
- $percentage_format = number_format( $percentage, 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] );
1860
- $balloon_text = __( 'c-' . $a_country[ 'country' ], 'wp-slimstat' ) . ': ' . $percentage_format . '% (' . number_format( $a_country[ 'counthits' ], 0, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) . ')';
1861
- $data_areas[ $a_country[ 'country' ] ] = '{id:"' . strtoupper( $a_country[ 'country' ] ) . '",balloonText:"' . $balloon_text . '",value:' . $percentage . '}';
1862
-
1863
- if ( $percentage > $max ) {
1864
- $max = $percentage;
1865
- }
1866
- }
1867
-
1868
- $path_slimstat = dirname( dirname( __FILE__ ) );
1869
- wp_enqueue_script( 'slimstat_ammap', plugins_url( '/admin/js/ammap/ammap.js', $path_slimstat ), array(), null, false );
1870
- wp_enqueue_script( 'slimstat_ammap_world', plugins_url( '/admin/js/ammap/world.js', $path_slimstat ), array(), null, false );
1871
- wp_enqueue_script( 'slimstat_amcharts_plugins_export', plugins_url( '/admin/js/amcharts/plugins/export/export.min.js', $path_slimstat ), array( 'slimstat_ammap' ), null, false );
1872
- wp_enqueue_script( 'slimstat_amcharts_theme_light', plugins_url( '/admin/js/amcharts/themes/light.js', $path_slimstat ), array( 'slimstat_ammap' ), null, false );
1873
-
1874
- wp_enqueue_style( 'slimstat_amcharts_plugins_export_css', plugins_url( '/admin/js/amcharts/plugins/export/export.css', $path_slimstat ) );
1875
-
1876
- ?>
1877
-
1878
- <div id="map_slim_p6_01" style="height: 100%"></div>
1879
-
1880
- <script type="text/javascript">
1881
- jQuery( function() {
1882
- var targetSVG = "M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z";
1883
-
1884
- var dataProvider = {
1885
- map: "worldLow",
1886
- getAreasFromMap: true,
1887
- areas:[ <?php echo implode( ',', $data_areas ) ?> ],
1888
- images: [ <?php if ( !empty( $data_points ) ) echo implode( ',', $data_points ) ?> ]
1889
- };
1890
-
1891
- // Create AmMap object
1892
- var map = new AmCharts.AmMap();
1893
-
1894
- <?php if ($max != 0): ?>
1895
- var legend = new AmCharts.ValueLegend();
1896
- legend.height = 20;
1897
- legend.minValue = "0.01";
1898
- legend.maxValue = "<?php echo $max ?>%";
1899
- legend.right = 20;
1900
- legend.showAsGradient = true;
1901
- legend.width = 300;
1902
- map.valueLegend = legend;
1903
- <?php endif; ?>
1904
-
1905
- // Configuration
1906
- map.areasSettings = {
1907
- autoZoom: false,
1908
- color: "#9dff98",
1909
- colorSolid: "#fa8a50",
1910
- outlineColor: "#888888",
1911
- selectedColor: "#ffb739"
1912
- };
1913
- map.imagesSettings = {
1914
- rollOverColor: "#089282",
1915
- rollOverScale: 2,
1916
- selectedScale: 2,
1917
- selectedColor: "#089282",
1918
- color: "#13564e"
1919
- };
1920
- map.zoomControl = {
1921
- zoomControlEnabled: true,
1922
- zoomFactor: 3
1923
- };
1924
- map.export = {
1925
- "enabled": true,
1926
- "libs": {
1927
- "path": "<?php echo plugins_url('/js/amcharts/plugins/export/libs/', dirname(__FILE__)) ?>"
1928
- },
1929
- "menu": [ {
1930
- "class": "export-main",
1931
- "menu": [ {
1932
- "label": "Download as...",
1933
- "menu": [ "PNG", "PDF" ]
1934
- } ]
1935
- } ]
1936
- };
1937
- map.backgroundAlpha = .9;
1938
- map.backgroundColor = "#7adafd";
1939
- map.backgroundZoomsToTop = false;
1940
- map.balloon.color = "#000000";
1941
- map.colorSteps = 5;
1942
- map.mouseWheelZoomEnabled = true;
1943
- map.pathToImages = "<?php echo plugins_url('/js/ammap/images/', dirname(__FILE__)) ?>";
1944
-
1945
- // Init Data
1946
- map.dataProvider = dataProvider;
1947
-
1948
- // Display Map
1949
- map.write( "map_slim_p6_01" );
1950
- });
1951
- </script><?php
1952
-
1953
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1954
- die();
1955
- }
1956
- }
1957
-
1958
- public static function get_search_terms_info( $_searchterms = '', $_referer = '', $_serp_only = false ) {
1959
- $query_details = '';
1960
- $search_terms_info = '';
1961
- parse_str( $_referer, $query_parse_str );
1962
-
1963
- if ( !empty( $query_parse_str[ 'source' ] ) && !$_serp_only ) {
1964
- $query_details = __( 'src', 'wp-slimstat' ) . ": {$query_parse_str[ 'source' ]}";
1965
- }
1966
-
1967
- if ( !empty( $query_parse_str[ 'cd' ] ) ) {
1968
- $query_details = __( 'serp', 'wp-slimstat') . ": {$query_parse_str[ 'cd' ]}";
1969
- }
1970
-
1971
- if ( !empty( $query_details ) ) {
1972
- $query_details = "($query_details)";
1973
- }
1974
-
1975
- if ( !empty( $_searchterms ) && $_searchterms != '_' ) {
1976
- $search_terms_info = '<a class="slimstat-font-logout" target="_blank" title="' . htmlentities( __( 'Go to the referring page', 'wp-slimstat' ), ENT_QUOTES, 'UTF-8' ) . '" href="' . $_referer . '"></a>' . htmlentities( $_searchterms, ENT_QUOTES, 'UTF-8' );
1977
- $search_terms_info = "$search_terms_info $query_details";
1978
- }
1979
- return $search_terms_info;
1980
- }
1981
-
1982
- /**
1983
- * Generate the HTML that lists all the filters currently used
1984
- */
1985
- public static function get_filters_html( $_filters_array = array() ) {
1986
- $filters_html = '';
1987
-
1988
- if ( !empty( $_filters_array ) ) {
1989
- foreach( $_filters_array as $a_filter_label => $a_filter_details ) {
1990
- if ( !array_key_exists( $a_filter_label, wp_slimstat_db::$columns_names ) || strpos( $a_filter_label, 'no_filter' ) !== false ) {
1991
- continue;
1992
- }
1993
-
1994
- $a_filter_value_no_slashes = htmlentities( str_replace( '\\','', $a_filter_details[ 1 ] ), ENT_QUOTES, 'UTF-8' );
1995
- $filters_html .= '<li>' . strtolower( wp_slimstat_db::$columns_names[ $a_filter_label ][ 0 ] ) . ' ' . __( str_replace( '_', ' ', $a_filter_details[ 0 ] ), 'wp-slimstat' ) . " $a_filter_value_no_slashes <a class='slimstat-filter-link slimstat-font-cancel' title='" . htmlentities( __( 'Remove filter for', 'wp-slimstat' ), ENT_QUOTES, 'UTF-8' ) . ' ' . wp_slimstat_db::$columns_names[ $a_filter_label ][ 0 ] . "' href='" . self::fs_url( "$a_filter_label equals " ) . "'></a></li>";
1996
- }
1997
- }
1998
-
1999
- if ( !empty( $filters_html ) ) {
2000
- $filters_html = "<ul class='slimstat-filter-list'>$filters_html</ul><a href='#' id='slimstat-save-filter' class='slimstat-filter-action-button button-secondary noslimstat' data-filter-array='" . htmlentities( json_encode( $_filters_array ), ENT_QUOTES, 'UTF-8' ) . "'>" . __( 'Save', 'wp-slimstat' ) . '</a>';
2001
- }
2002
-
2003
- if ( count( $_filters_array ) > 1 ) {
2004
- $filters_html .= '<a href="' . self::fs_url() . '" id="slimstat-remove-all-filters" class="button-secondary slimstat-filter-action-button noslimstat">' . __( 'Reset All', 'wp-slimstat' ) . '</a>';
2005
- }
2006
-
2007
- return $filters_html;
2008
- }
2009
-
2010
- public static function fs_url( $_filters_string = '' ) {
2011
- // Allow only legitimate requests
2012
- if ( !is_admin() ) {
2013
- return '';
2014
- }
2015
-
2016
- $request_uri = admin_url( 'admin.php' );
2017
-
2018
- $request_page = 'slimview1';
2019
- if ( !empty( $_REQUEST[ 'page' ] ) ) {
2020
- if ( !array_key_exists( $_REQUEST[ 'page' ], wp_slimstat_admin::$screens_info ) ) {
2021
- return '';
2022
- }
2023
-
2024
- $request_page = $_REQUEST[ 'page' ];
2025
- }
2026
-
2027
- $request_uri .= '?page=' . $request_page;
2028
-
2029
- // Avoid XSS attacks ( why would the owner try to hack into his/her own website though? )
2030
- if ( !empty( $_SERVER[ 'HTTP_REFERER' ] ) ) {
2031
- $parsed_referer = parse_url( $_SERVER[ 'HTTP_REFERER' ] );
2032
- if ( !$parsed_referer || ( !empty( $parsed_referer[ 'scheme' ] ) && !in_array( strtolower( $parsed_referer[ 'scheme' ] ), array( 'http', 'https' ) ) ) ) {
2033
- return '';
2034
- }
2035
- }
2036
-
2037
- $filters_normalized = wp_slimstat_db::parse_filters( $_filters_string );
2038
-
2039
- // Columns
2040
- if ( !empty( $filters_normalized[ 'columns' ] ) ) {
2041
- foreach ( $filters_normalized[ 'columns' ] as $a_key => $a_filter ) {
2042
- $a_key = str_replace( '_calculated', '', $a_key );
2043
- $request_uri .= "&amp;fs%5B$a_key%5D=" . urlencode( $a_filter[ 0 ] . ' ' . $a_filter[ 1 ] );
2044
- }
2045
- }
2046
-
2047
- // Date ranges
2048
- if ( !empty( $filters_normalized[ 'date' ] ) ) {
2049
- foreach ( $filters_normalized[ 'date' ] as $a_key => $a_filter ) {
2050
- if ( isset( $a_filter ) ) {
2051
- $request_uri .= "&amp;fs%5B$a_key%5D=" . urlencode( 'equals ' . $a_filter );
2052
- }
2053
- }
2054
- }
2055
-
2056
- // Misc filters
2057
- if ( !empty( $filters_normalized[ 'misc' ] ) ) {
2058
- foreach ( $filters_normalized[ 'misc' ] as $a_key => $a_filter ) {
2059
- $request_uri .= "&amp;fs%5B$a_key%5D=" . urlencode( 'equals ' . $a_filter );
2060
- }
2061
- }
2062
-
2063
- return $request_uri;
2064
- }
2065
-
2066
- /**
2067
- * Attempts to convert a permalink into a post title
2068
- */
2069
- public static function get_resource_title( $_resource = '' ) {
2070
- $resource_title = $_resource;
2071
-
2072
- if ( wp_slimstat::$settings[ 'convert_resource_urls_to_titles' ] != 'on' ) {
2073
- return htmlentities( urldecode( $resource_title ), ENT_QUOTES, 'UTF-8' );
2074
- }
2075
-
2076
- // Is this a post or a page?
2077
- $post_id = url_to_postid( $_resource );
2078
-
2079
- if ( $post_id > 0 ) {
2080
- $resource_title = the_title_attribute( array( 'post' => $post_id, 'echo' => false ) );
2081
-
2082
- // Encode URLs to avoid XSS attacks
2083
- if ( $resource_title == $_resource ) {
2084
- $resource_title = htmlspecialchars( $resource_title, ENT_QUOTES, 'UTF-8' );
2085
- }
2086
- }
2087
-
2088
- // Is this a category or tag permalink?
2089
- else {
2090
- $term_names = array();
2091
- $home_url = get_home_url();
2092
- $relative_home = parse_url( $home_url, PHP_URL_PATH );
2093
-
2094
- $all_terms = get_terms( 'category' );
2095
- foreach ( $all_terms as $a_term ) {
2096
- $term_link = get_term_link( $a_term, 'category' );
2097
- if ( !is_wp_error( $term_link ) && str_replace( $home_url, $relative_home, $term_link ) == $_resource ) {
2098
- $term_names[] = $a_term->name;
2099
- }
2100
- }
2101
-
2102
- $all_terms = get_terms( 'tag' );
2103
- foreach ( $all_terms as $a_term ) {
2104
- $term_link = get_term_link( $a_term, 'tag' );
2105
- if ( !is_wp_error( $term_link ) && str_replace( $home_url, $relative_home, $term_link ) == $_resource ) {
2106
- $term_names[] = $a_term->name;
2107
- }
2108
- }
2109
-
2110
- if ( !empty( $term_names ) ) {
2111
- $resource_title = implode( ',', $term_names );
2112
- }
2113
- else {
2114
- $resource_title = htmlspecialchars( $resource_title, ENT_QUOTES, 'UTF-8' );
2115
- }
2116
- }
2117
-
2118
- return $resource_title;
2119
- }
2120
-
2121
- public static function inline_help( $_text = '', $_echo = true ) {
2122
- if ( is_admin() && !empty( $_text ) ) {
2123
- $wrapped_text = "<i class='slimstat-tooltip-trigger corner'><span class='slimstat-tooltip-content'>$_text</span></i>";
2124
- }
2125
- else {
2126
- $wrapped_text = '';
2127
- }
2128
-
2129
- if ($_echo)
2130
- echo $wrapped_text;
2131
- else
2132
- return $wrapped_text;
2133
- }
2134
-
2135
- protected static function _check_args( $_args = array() ) {
2136
-
2137
- // When called from the WP Dashboard, the action passes 2 arguments: post_id (empty) and array of arguments
2138
- if ( !empty( $_args[ 1 ] ) ) {
2139
- $_args = $_args[ 1 ];
2140
- }
2141
- // When called from within Slimstat, the first argument is the array of arguments for the callback
2142
- else {
2143
- $_args = $_args[ 0 ];
2144
- }
2145
-
2146
- if ( !is_array( $_args ) ) {
2147
- $_args = array();
2148
- }
2149
-
2150
- $report_id = 0;
2151
-
2152
- // Is this an Ajax request?
2153
- if ( !empty( $_POST[ 'report_id' ] ) ) {
2154
- check_ajax_referer( 'meta-box-order', 'security' ); // Let's make sure the request is coming from an authorized source
2155
- $report_id = $_POST[ 'report_id' ];
2156
- }
2157
-
2158
- // When on the WP Dashboard, the action sets the 'id' key of the $_args array
2159
- else if ( !empty( $_args[ 'id' ] ) ){
2160
- $report_id = $_args[ 'id' ];
2161
- }
2162
-
2163
- // Honor the 'hidden' attribute, but not on the WP Dashboard ( empty( $_args[ 'id' ] ) )
2164
- // if ( empty( $report_id ) || in_array( 'hidden', self::$reports_info[ $report_id ][ 'classes' ] ) ) {
2165
- // return array();
2166
- // }
2167
-
2168
- if ( !empty( self::$reports_info[ $report_id ] ) && is_array( self::$reports_info[ $report_id ] ) ) {
2169
- // Default values
2170
- $_args = array_merge( array(
2171
- 'title' => '',
2172
- 'callback' => '',
2173
- 'callback_args' => array(),
2174
- 'classes' => array(),
2175
- 'screens' => array(),
2176
- 'tooltip' => ''
2177
- ), self::$reports_info[ $report_id ] );
2178
- }
2179
-
2180
- // Default callback args
2181
- $_args[ 'callback_args' ] = array_merge( array(
2182
- 'type' => '',
2183
- 'columns' => '',
2184
- 'where' => '',
2185
- 'having' => '',
2186
- 'as_column' => '',
2187
- 'filter_op' => 'equals',
2188
- 'outer_select_column' => '',
2189
- 'aggr_function' => 'MAX',
2190
- 'use_date_filters' => true,
2191
- 'results_per_page' => wp_slimstat::$settings[ 'rows_to_show' ],
2192
- 'criteria' => ''
2193
- ), $_args[ 'callback_args' ] );
2194
-
2195
- return $_args;
2196
- }
2197
- }
1
+ <?php
2
+
3
+ class wp_slimstat_reports {
4
+
5
+ // Structures to store all the information about what screens and reports are available
6
+ public static $reports_info = array();
7
+ public static $user_reports = array();
8
+
9
+ // Useful data for the reports
10
+ protected static $pageviews = 0;
11
+
12
+ /**
13
+ * Initalize class properties
14
+ */
15
+ public static function init(){
16
+
17
+ // Include and initialize the API to interact with the database
18
+ include_once( 'wp-slimstat-db.php' );
19
+ wp_slimstat_db::init();
20
+
21
+ // Retrieve data that will be used by multiple reports
22
+ self::$pageviews = wp_slimstat_db::count_records();
23
+
24
+ // Define all the reports
25
+ //
26
+ // Parameters
27
+ // - title : report name
28
+ // - callback : function to use to render the report
29
+ // - callback_args : parameters to pass to the function
30
+ // - classes : determine the look and feel of this report ( tall, large, extralarge, full-width, hidden )
31
+ // - screens : where should the report appear ( slimview1, .., slimview4, dashboard )
32
+ // - tooltip : contextual help to be displayed on hover
33
+
34
+ $chart_tooltip = '<strong>' . __( 'Chart controls', 'wp-slimstat' ) . '</strong><ul><li>' . __( 'Use your mouse wheel to zoom in and out', 'wp-slimstat' ) . '</li><li>' . __( 'While zooming in, drag the chart to move to a different area', 'wp-slimstat' ) . '</li></ul>';
35
+
36
+ self::$reports_info = array(
37
+ 'slim_p7_02' => array(
38
+ 'title' => __( 'Visitors Activity', 'wp-slimstat' ),
39
+ 'callback' => array( __CLASS__, 'show_activity_log' ),
40
+ 'callback_args' => array(
41
+ 'type' => 'recent',
42
+ 'columns' => '*',
43
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
44
+ ),
45
+ 'classes' => array( 'full-width', 'tall' ),
46
+ 'screens' => array( 'slimview1', 'dashboard' ),
47
+ 'tooltip' => __( 'Color codes', 'wp-slimstat' ).'</strong><p><span class="little-color-box is-search-engine"></span> '.__( 'From search result page', 'wp-slimstat' ).'</p><p><span class="little-color-box is-known-visitor"></span> '.__( 'Known Visitor', 'wp-slimstat' ).'</p><p><span class="little-color-box is-known-user"></span> '.__( 'Known Users', 'wp-slimstat' ).'</p><p><span class="little-color-box is-direct"></span> '.__( 'Other Humans', 'wp-slimstat' ).'</p><p><span class="little-color-box"></span> '.__( 'Bot or Crawler', 'wp-slimstat' ).'</p>'
48
+ ),
49
+
50
+ 'slim_p1_01' => array(
51
+ 'title' => __( 'Pageviews', 'wp-slimstat' ),
52
+ 'callback' => array( __CLASS__, 'show_chart' ),
53
+ 'callback_args' => array(
54
+ 'id' => 'slim_p1_01',
55
+ 'chart_data' => array(
56
+ 'data1' => 'COUNT( ip )',
57
+ 'data2' => 'COUNT( DISTINCT ip )'
58
+ ),
59
+ 'chart_labels' => array(
60
+ __( 'Pageviews', 'wp-slimstat' ),
61
+ __( 'Unique IPs', 'wp-slimstat' )
62
+ )
63
+ ),
64
+ 'classes' => array( 'extralarge', 'chart' ),
65
+ 'screens' => array( 'slimview2', 'dashboard' ),
66
+ 'tooltip' => $chart_tooltip
67
+ ),
68
+ 'slim_p1_02' => array(
69
+ 'title' => __( 'About Slimstat', 'wp-slimstat' ),
70
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
71
+ 'callback_args' => array(
72
+ 'raw' => array( __CLASS__, 'get_about_wpslimstat' )
73
+ ),
74
+ 'classes' => array( 'normal', 'hidden' ),
75
+ 'screens' => array( 'slimview2' )
76
+ ),
77
+ 'slim_p1_03' => array(
78
+ 'title' => __( 'Traffic at a Glance', 'wp-slimstat' ),
79
+ // 'callback' => array( __CLASS__, 'show_overview_summary' ),
80
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
81
+ 'callback_args' => array(
82
+ 'raw' => array( __CLASS__, 'get_overview_summary' )
83
+ ),
84
+ 'classes' => array( 'normal' ),
85
+ 'screens' => array( 'slimview2', 'dashboard' )
86
+ ),
87
+ 'slim_p1_04' => array(
88
+ 'title' => __( 'Currently Online', 'wp-slimstat' ),
89
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
90
+ 'callback_args' => array(
91
+ 'type' => 'recent',
92
+ 'columns' => 'ip',
93
+ 'where' => 'dt_out > '. ( date_i18n( 'U' ) - 300 ) . ' OR dt > '. ( date_i18n( 'U' ) - 300 ),
94
+ 'use_date_filters' => false,
95
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
96
+ ),
97
+ 'classes' => array( 'normal' ),
98
+ 'screens' => array( 'slimview2', 'dashboard' )
99
+ ),
100
+ 'slim_p1_06' => array(
101
+ 'title' => __( 'Recent Search Terms', 'wp-slimstat' ),
102
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
103
+ 'callback_args' => array(
104
+ 'type' => 'recent',
105
+ 'columns' => 'searchterms',
106
+ 'where' => 'searchterms <> "_" AND searchterms <> "" AND searchterms IS NOT NULL',
107
+ 'more_columns' => 'referer, resource',
108
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
109
+ ),
110
+ 'classes' => array( 'normal' ),
111
+ 'screens' => array( 'slimview2', 'slimview5' ),
112
+ 'tooltip' => __( 'Keywords used by your visitors to find your website on a search engine.', 'wp-slimstat' )
113
+ ),
114
+ 'slim_p1_08' => array(
115
+ 'title' => __( 'Top Web Pages', 'wp-slimstat' ),
116
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
117
+ 'callback_args' => array(
118
+ 'type' => 'top',
119
+ 'columns' => 'SUBSTRING_INDEX(resource, "' . ( !get_option( 'permalink_structure' ) ? '&' : '?' ) . '", 1)',
120
+ 'as_column' => 'resource_calculated',
121
+ 'filter_op' => 'contains',
122
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
123
+ ),
124
+ 'classes' => array( 'normal' ),
125
+ 'screens' => array( 'slimview2', 'dashboard' ),
126
+ 'tooltip' => __( 'Here a "page" is not just a WordPress page type, but any webpage on your site, including posts, products, categories, and so on. You can set the corresponding filter where Resource Content Type equals cpt:you_cpt_slug_here to get top web pages for a specific custom post type you have.', 'wp-slimstat' )
127
+ ),
128
+ 'slim_p1_10' => array(
129
+ 'title' => __('Top Referring Domains', 'wp-slimstat'),
130
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
131
+ 'callback_args' => array(
132
+ 'type' => 'top',
133
+ 'columns' => 'REPLACE( SUBSTRING_INDEX( ( SUBSTRING_INDEX( ( SUBSTRING_INDEX( referer, "://", -1 ) ), "/", 1 ) ), ".", -5 ), "www.", "" )',
134
+ 'as_column' => 'referer_calculated',
135
+ 'filter_op' => 'contains',
136
+ 'where' => 'referer NOT LIKE "%' . str_replace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) ) . '%"',
137
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
138
+ ),
139
+ 'classes' => array( 'normal' ),
140
+ 'screens' => array( 'slimview2', 'slimview5', 'dashboard' )
141
+ ),
142
+ 'slim_p1_11' => array(
143
+ 'title' => __( 'Top Known Visitors', 'wp-slimstat' ),
144
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
145
+ 'callback_args' => array(
146
+ 'type' => 'top',
147
+ 'columns' => 'username',
148
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
149
+ ),
150
+ 'classes' => array( 'normal', 'hidden' ),
151
+ 'screens' => array( 'slimview2', 'dashboard' )
152
+ ),
153
+ 'slim_p1_12' => array(
154
+ 'title' => __( 'Top Search Terms', 'wp-slimstat' ),
155
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
156
+ 'callback_args' => array(
157
+ 'type' => 'top',
158
+ 'columns' => 'searchterms',
159
+ 'where' => 'searchterms <> "_" AND searchterms <> "" AND searchterms IS NOT NULL',
160
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
161
+ ),
162
+ 'classes' => array( 'normal' ),
163
+ 'screens' => array( 'slimview2', 'slimview4', 'slimview5', 'dashboard' )
164
+ ),
165
+ 'slim_p1_13' => array(
166
+ 'title' => __( 'Top Countries', 'wp-slimstat' ),
167
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
168
+ 'callback_args' => array(
169
+ 'type' => 'top',
170
+ 'columns' => 'country',
171
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
172
+ ),
173
+ 'classes' => array( 'normal', 'hidden' ),
174
+ 'screens' => array( 'slimview2', 'slimview3', 'slimview5', 'dashboard' ),
175
+ 'tooltip' => __( 'You can configure Slimstat to ignore a specific Country by setting the corresponding filter under Settings > Slimstat > Filters.', 'wp-slimstat' )
176
+ ),
177
+ 'slim_p1_15' => array(
178
+ 'title' => __( 'Rankings', 'wp-slimstat' ),
179
+ 'callback' => array( __CLASS__, 'show_rankings' ),
180
+ 'callback_args' => array(
181
+ 'id' => 'slim_p1_15'
182
+ ),
183
+ 'classes' => array( 'normal', 'hidden' ),
184
+ 'screens' => array( 'slimview2' ),
185
+ 'tooltip' => __( "Slimstat retrieves live information from Alexa, Facebook and Mozscape, to measures your site's rankings. Values are updated every 12 hours. Please enter your personal access ID in the settings to access your personalized Mozscape data.", 'wp-slimstat' )
186
+ ),
187
+ 'slim_p1_17' => array(
188
+ 'title' => __( 'Top Language Families', 'wp-slimstat' ),
189
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
190
+ 'callback_args' => array(
191
+ 'type' => 'top',
192
+ 'columns' => 'SUBSTRING(language, 1, 2)',
193
+ 'as_column' => 'language_calculated',
194
+ 'filter_op' => 'contains',
195
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
196
+ ),
197
+ 'classes' => array( 'normal', 'hidden' ),
198
+ 'screens' => array( 'slimview3' )
199
+ ),
200
+ 'slim_p1_18' => array(
201
+ 'title' => __( 'Users Currently Online', 'wp-slimstat' ),
202
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
203
+ 'callback_args' => array(
204
+ 'type' => 'recent',
205
+ 'columns' => 'username',
206
+ 'where' => 'dt_out > '. ( date_i18n( 'U' ) - 300 ) . ' OR dt > '. ( date_i18n( 'U' ) - 300 ),
207
+ 'use_date_filters' => false,
208
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
209
+ ),
210
+ 'classes' => array( 'normal', 'hidden' ),
211
+ 'screens' => array( 'slimview2', 'dashboard' ),
212
+ 'tooltip' => __( 'When visitors leave a comment on your blog, WordPress assigns them a cookie. Slimstat leverages this information to identify returning visitors. Please note that visitors also include registered users.', 'wp-slimstat' )
213
+ ),
214
+ 'slim_p1_19_01' => array( // Chart Reports need to always have a _01 suffix to tell our custom "refresh" code to avoid fading the chart, which apparently doesn't work
215
+ 'title' => __( 'Search Terms', 'wp-slimstat' ),
216
+ 'callback' => array( __CLASS__, 'show_chart' ),
217
+ 'callback_args' => array(
218
+ 'id' => 'slim_p1_19_01',
219
+ 'chart_data' => array(
220
+ 'data1' => 'COUNT( searchterms )',
221
+ 'data2' => 'COUNT( DISTINCT searchterms )',
222
+ 'where' => 'searchterms <> "_" AND searchterms IS NOT NULL AND searchterms <> ""'
223
+ ),
224
+ 'chart_labels' => array(
225
+ __( 'Search Terms', 'wp-slimstat' ),
226
+ __( 'Unique Terms', 'wp-slimstat' )
227
+ )
228
+ ),
229
+ 'classes' => array( 'extralarge', 'chart' ),
230
+ 'screens' => array( 'slimview2' ),
231
+ 'tooltip' => $chart_tooltip
232
+ ),
233
+ 'slim_p1_20' => array(
234
+ 'title' => __('Top Referring URLs', 'wp-slimstat'),
235
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
236
+ 'callback_args' => array(
237
+ 'type' => 'top',
238
+ 'columns' => 'referer',
239
+ 'where' => 'referer NOT LIKE "%' . str_replace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) ) . '%"',
240
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
241
+ ),
242
+ 'classes' => array( 'normal' ),
243
+ 'screens' => array( 'slimview2', 'slimview5', 'dashboard' )
244
+ ),
245
+
246
+ 'slim_p2_01' => array(
247
+ 'title' => __( 'Human Visits', 'wp-slimstat' ),
248
+ 'callback' => array( __CLASS__, 'show_chart' ),
249
+ 'callback_args' => array(
250
+ 'id' => 'slim_p2_01',
251
+ 'chart_data' => array(
252
+ 'data1' => 'COUNT( DISTINCT visit_id )',
253
+ 'data2' => 'COUNT( DISTINCT ip )',
254
+ 'where' => '(visit_id > 0 AND browser_type <> 1)'
255
+ ),
256
+ 'chart_labels' => array(
257
+ __( 'Visits', 'wp-slimstat' ),
258
+ __( 'Unique IPs', 'wp-slimstat' )
259
+ )
260
+ ),
261
+ 'classes' => array( 'extralarge', 'chart' ),
262
+ 'screens' => array( 'slimview3' ),
263
+ 'tooltip' => $chart_tooltip
264
+ ),
265
+ 'slim_p2_02' => array(
266
+ 'title' => __( 'Audience Overview', 'wp-slimstat' ),
267
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
268
+ 'callback_args' => array(
269
+ 'raw' => array( __CLASS__, 'get_visitors_summary' )
270
+ ),
271
+ 'classes' => array( 'normal' ),
272
+ 'screens' => array( 'slimview3', 'dashboard' ),
273
+ 'tooltip' => __( 'Where not otherwise specified, the metrics in this report are referred to human visitors.', 'wp-slimstat' )
274
+ ),
275
+ 'slim_p2_03' => array(
276
+ 'title' => __( 'Top Languages', 'wp-slimstat' ),
277
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
278
+ 'callback_args' => array(
279
+ 'type' => 'top',
280
+ 'columns' => 'language',
281
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
282
+ ),
283
+ 'classes' => array( 'normal' ),
284
+ 'screens' => array( 'slimview3' )
285
+ ),
286
+ 'slim_p2_04' => array(
287
+ 'title' => __( 'Top Browsers', 'wp-slimstat' ),
288
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
289
+ 'callback_args' => array(
290
+ 'type' => 'top',
291
+ 'columns' => 'browser, browser_version',
292
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
293
+ ),
294
+ 'classes' => array( 'normal' ),
295
+ 'screens' => array( 'slimview3', 'dashboard' )
296
+ ),
297
+ 'slim_p2_05' => array(
298
+ 'title' => __( 'Top Service Providers', 'wp-slimstat' ),
299
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
300
+ 'callback_args' => array(
301
+ 'type' => 'top',
302
+ 'columns' => 'ip',
303
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
304
+ ),
305
+ 'classes' => array( 'extralarge', 'hidden' ),
306
+ 'screens' => array( 'slimview3' ),
307
+ 'tooltip' => __( 'Internet Service Provider: a company which provides other companies or individuals with access to the Internet. Your DSL or cable internet service is provided to you by your ISP.<br><br>You can ignore specific IP addresses by setting the corresponding filter under Settings > Slimstat > Filters.', 'wp-slimstat' )
308
+ ),
309
+ 'slim_p2_06' => array(
310
+ 'title' => __( 'Top Operating Systems', 'wp-slimstat' ),
311
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
312
+ 'callback_args' => array(
313
+ 'type' => 'top',
314
+ 'columns' => 'platform',
315
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
316
+ ),
317
+ 'classes' => array( 'normal', 'hidden' ),
318
+ 'screens' => array( 'slimview3' ),
319
+ 'tooltip' => __( 'Internet Service Provider: a company which provides other companies or individuals with access to the Internet. Your DSL or cable internet service is provided to you by your ISP.<br><br>You can ignore specific IP addresses by setting the corresponding filter under Settings > Slimstat > Filters.', 'wp-slimstat' )
320
+ ),
321
+ 'slim_p2_07' => array(
322
+ 'title' => __( 'Top Screen Resolutions', 'wp-slimstat' ),
323
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
324
+ 'callback_args' => array(
325
+ 'type' => 'top',
326
+ 'columns' => 'screen_width, screen_height',
327
+ 'where' => 'screen_width <> 0 AND screen_height <> 0',
328
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
329
+ ),
330
+ 'classes' => array( 'normal' ),
331
+ 'screens' => array( 'slimview3', 'dashboard' )
332
+ ),
333
+ 'slim_p2_08' => array(
334
+ 'title' => __( 'Top Viewport Sizes', 'wp-slimstat' ),
335
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
336
+ 'callback_args' => array(
337
+ 'type' => 'top',
338
+ 'columns' => 'resolution',
339
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
340
+ ),
341
+ 'classes' => array( 'normal', 'hidden' ),
342
+ 'screens' => array( 'slimview3' )
343
+ ),
344
+ 'slim_p2_09' => array(
345
+ 'title' => __( 'Browser Capabilities', 'wp-slimstat' ),
346
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
347
+ 'callback_args' => array(
348
+ 'raw' => array( __CLASS__, 'get_plugins' )
349
+ ),
350
+ 'classes' => array( 'normal', 'hidden' ),
351
+ 'screens' => array( 'slimview3' )
352
+ ),
353
+ 'slim_p2_12' => array(
354
+ 'title' => __( 'Visit Duration', 'wp-slimstat' ),
355
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
356
+ 'callback_args' => array(
357
+ 'raw' => array( __CLASS__, 'get_visits_duration' )
358
+ ),
359
+ 'classes' => array( 'normal', 'hidden' ),
360
+ 'screens' => array( 'slimview3' ),
361
+ 'tooltip' => __( 'All values represent the percentages of pageviews within the corresponding time range.', 'wp-slimstat' )
362
+ ),
363
+ 'slim_p2_13' => array(
364
+ 'title' => __( 'Recent Countries', 'wp-slimstat' ),
365
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
366
+ 'callback_args' => array(
367
+ 'type' => 'recent',
368
+ 'columns' => 'country',
369
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
370
+ ),
371
+ 'classes' => array( 'normal', 'hidden' ),
372
+ 'screens' => array( 'slimview3', 'slimview5' )
373
+ ),
374
+ 'slim_p2_14' => array(
375
+ 'title' => __( 'Recent Viewport Sizes', 'wp-slimstat' ),
376
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
377
+ 'callback_args' => array(
378
+ 'type' => 'recent',
379
+ 'columns' => 'resolution',
380
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
381
+ ),
382
+ 'classes' => array( 'normal', 'hidden' ),
383
+ 'screens' => array( 'slimview3' )
384
+ ),
385
+ 'slim_p2_15' => array(
386
+ 'title' => __( 'Recent Operating Systems', 'wp-slimstat' ),
387
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
388
+ 'callback_args' => array(
389
+ 'type' => 'recent',
390
+ 'columns' => 'platform',
391
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
392
+ ),
393
+ 'classes' => array( 'normal', 'hidden' ),
394
+ 'screens' => array( 'slimview3' )
395
+ ),
396
+ 'slim_p2_16' => array(
397
+ 'title' => __( 'Recent Browsers', 'wp-slimstat' ),
398
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
399
+ 'callback_args' => array(
400
+ 'type' => 'recent',
401
+ 'columns' => 'browser, browser_version',
402
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
403
+ ),
404
+ 'classes' => array( 'normal', 'hidden' ),
405
+ 'screens' => array( 'slimview3' )
406
+ ),
407
+ 'slim_p2_17' => array(
408
+ 'title' => __( 'Recent Languages', 'wp-slimstat' ),
409
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
410
+ 'callback_args' => array(
411
+ 'type' => 'recent',
412
+ 'columns' => 'language',
413
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
414
+ ),
415
+ 'classes' => array( 'normal', 'hidden' ),
416
+ 'screens' => array( 'slimview3' )
417
+ ),
418
+ 'slim_p2_18' => array(
419
+ 'title' => __( 'Top Browser Families', 'wp-slimstat' ),
420
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
421
+ 'callback_args' => array(
422
+ 'type' => 'top',
423
+ 'columns' => 'browser',
424
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
425
+ ),
426
+ 'classes' => array( 'normal', 'hidden' ),
427
+ 'screens' => array( 'slimview3' ),
428
+ 'tooltip' => __( 'This report shows you what user agent families (no version considered) are popular among your visitors.', 'wp-slimstat' )
429
+ ),
430
+ 'slim_p2_19' => array(
431
+ 'title' => __( 'Top OS Families', 'wp-slimstat' ),
432
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
433
+ 'callback_args' => array(
434
+ 'type' => 'top',
435
+ 'columns' => 'CONCAT("p-", SUBSTRING(platform, 1, 3))',
436
+ 'as_column' => 'platform_calculated',
437
+ 'filter_op' => 'contains',
438
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
439
+ ),
440
+ 'classes' => array( 'normal' ),
441
+ 'screens' => array( 'slimview3' ),
442
+ 'tooltip' => __( 'This report shows you what operating system families (no version considered) are popular among your visitors.', 'wp-slimstat' )
443
+ ),
444
+ 'slim_p2_20' => array(
445
+ 'title' => __( 'Recent Users', 'wp-slimstat' ),
446
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
447
+ 'callback_args' => array(
448
+ 'type' => 'recent',
449
+ 'columns' => 'username',
450
+ 'where' => 'notes LIKE "%user:%"',
451
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
452
+ ),
453
+ 'classes' => array( 'normal' ),
454
+ 'screens' => array( 'slimview3' )
455
+ ),
456
+ 'slim_p2_21' => array(
457
+ 'title' => __( 'Top Users', 'wp-slimstat' ),
458
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
459
+ 'callback_args' => array(
460
+ 'type' => 'top',
461
+ 'columns' => 'username',
462
+ 'where' => 'notes LIKE "%user:%"',
463
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
464
+ ),
465
+ 'classes' => array( 'normal', 'hidden' ),
466
+ 'screens' => array( 'slimview3', 'dashboard' )
467
+ ),
468
+ 'slim_p2_22_01' => array( // Chart Reports need to always have a _01 suffix to tell our custom "refresh" code to avoid fading the chart, which apparently doesn't work
469
+ 'title' => __( 'Users', 'wp-slimstat' ),
470
+ 'callback' => array( __CLASS__, 'show_chart' ),
471
+ 'callback_args' => array(
472
+ 'id' => 'slim_p2_22_01',
473
+ 'chart_data' => array(
474
+ 'data1' => 'COUNT( username )',
475
+ 'data2' => 'COUNT( DISTINCT username )'
476
+ ),
477
+ 'chart_labels' => array(
478
+ __( 'Users', 'wp-slimstat' ),
479
+ __( 'Unique Users', 'wp-slimstat' )
480
+ )
481
+ ),
482
+ 'classes' => array( 'extralarge', 'chart' ),
483
+ 'screens' => array( 'slimview3' ),
484
+ 'tooltip' => $chart_tooltip
485
+ ),
486
+
487
+ 'slim_p3_01' => array(
488
+ 'title' => __( 'Traffic Sources', 'wp-slimstat' ),
489
+ 'callback' => array( __CLASS__, 'show_chart' ),
490
+ 'callback_args' => array(
491
+ 'id' => 'slim_p3_01',
492
+ 'chart_data' => array(
493
+ 'data1' => 'COUNT( DISTINCT referer )',
494
+ 'data2' => 'COUNT( DISTINCT ip )',
495
+ 'where' => '(referer IS NOT NULL AND referer NOT LIKE "%' . home_url() . '%")'
496
+ ),
497
+ 'chart_labels' => array(
498
+ __( 'Domains', 'wp-slimstat' ),
499
+ __( 'Unique IPs', 'wp-slimstat' )
500
+ )
501
+ ),
502
+ 'classes' => array( 'extralarge', 'chart' ),
503
+ 'screens' => array( 'slimview5' ),
504
+ 'tooltip' => $chart_tooltip
505
+ ),
506
+ 'slim_p3_02' => array(
507
+ 'title' => __( 'Traffic Summary', 'wp-slimstat' ),
508
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
509
+ 'callback_args' => array(
510
+ 'raw' => array( __CLASS__, 'get_traffic_sources_summary' )
511
+ ),
512
+ 'classes' => array( 'normal' ),
513
+ 'screens' => array( 'slimview5' )
514
+ ),
515
+ 'slim_p3_06' => array(
516
+ 'title' => __( 'Top Referring Search Engines', 'wp-slimstat' ),
517
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
518
+ 'callback_args' => array(
519
+ 'type' => 'top',
520
+ 'columns' => 'REPLACE( SUBSTRING_INDEX( SUBSTRING_INDEX( SUBSTRING_INDEX( referer, "://", -1 ), "/", 1 ), ".", -5 ), "www.", "" )',
521
+ 'as_column' => 'referer_calculated',
522
+ 'filter_op' => 'contains',
523
+ 'where' => 'searchterms IS NOT NULL AND searchterms <> "" AND searchterms <> "_" AND referer NOT LIKE "%' . str_replace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) ) . '%"',
524
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
525
+ ),
526
+ 'classes' => array( 'normal' ),
527
+ 'screens' => array( 'slimview5', 'dashboard' )
528
+ ),
529
+
530
+ 'slim_p4_01' => array(
531
+ 'title' => __( 'Recent Outbound Links', 'wp-slimstat' ),
532
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
533
+ 'callback_args' => array(
534
+ 'type' => 'recent',
535
+ 'columns' => 'outbound_resource',
536
+ 'raw' => array( 'wp_slimstat_db', 'get_recent_outbound' )
537
+ ),
538
+ 'classes' => array( 'large' ),
539
+ 'screens' => array( 'slimview4' ),
540
+ 'tooltip' => ''
541
+ ),
542
+ 'slim_p4_02' => array(
543
+ 'title' => __( 'Recent Posts', 'wp-slimstat' ),
544
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
545
+ 'callback_args' => array(
546
+ 'type' => 'recent',
547
+ 'columns' => 'resource',
548
+ 'where' => 'content_type = "post"',
549
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
550
+ ),
551
+ 'classes' => array( 'normal' ),
552
+ 'screens' => array( 'slimview4' )
553
+ ),
554
+ 'slim_p4_04' => array(
555
+ 'title' => __( 'Recent Feeds', 'wp-slimstat' ),
556
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
557
+ 'callback_args' => array(
558
+ 'type' => 'recent',
559
+ 'columns' => 'resource',
560
+ 'where' => '(resource LIKE "%/feed%" OR resource LIKE "%?feed=>%" OR resource LIKE "%&feed=>%" OR content_type LIKE "%feed%")',
561
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
562
+ ),
563
+ 'classes' => array( 'normal', 'hidden' ),
564
+ 'screens' => array( 'slimview4' )
565
+ ),
566
+ 'slim_p4_05' => array(
567
+ 'title' => __( 'Recent Pages Not Found', 'wp-slimstat' ),
568
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
569
+ 'callback_args' => array(
570
+ 'type' => 'recent',
571
+ 'columns' => 'resource',
572
+ 'where' => '(resource LIKE "[404]%" OR content_type LIKE "%404%")',
573
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
574
+ ),
575
+ 'classes' => array( 'normal' ),
576
+ 'screens' => array( 'slimview4' )
577
+ ),
578
+ 'slim_p4_06' => array(
579
+ 'title' => __( 'Recent Internal Searches', 'wp-slimstat' ),
580
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
581
+ 'callback_args' => array(
582
+ 'type' => 'recent',
583
+ 'columns' => 'searchterms',
584
+ 'where' => 'content_type LIKE "%search%" AND searchterms <> "" AND searchterms IS NOT NULL',
585
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
586
+ ),
587
+ 'classes' => array( 'normal', 'hidden' ),
588
+ 'screens' => array( 'slimview4' ),
589
+ 'tooltip' => __( "Searches performed using WordPress' built-in search functionality.", 'wp-slimstat' )
590
+ ),
591
+ 'slim_p4_07' => array(
592
+ 'title' => __( 'Top Categories', 'wp-slimstat' ),
593
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
594
+ 'callback_args' => array(
595
+ 'type' => 'top',
596
+ 'columns' => 'category',
597
+ 'where' => 'content_type LIKE "%category%"',
598
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
599
+ ),
600
+ 'classes' => array( 'normal' ),
601
+ 'screens' => array( 'slimview4', 'dashboard' )
602
+ ),
603
+
604
+ 'slim_p4_09' => array(
605
+ 'title' => __( 'Top Downloads', 'wp-slimstat' ),
606
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
607
+ 'callback_args' => array(
608
+ 'type' => 'top',
609
+ 'columns' => 'resource',
610
+ 'where' => 'content_type = "download"',
611
+ 'raw' => array( 'wp_slimstat_db', 'get_top' ),
612
+ 'criteria' => 'swap'
613
+ ),
614
+ 'classes' => array( 'large', 'hidden' ),
615
+ 'screens' => array( 'slimview4' ),
616
+ 'tooltip' => __( 'You can configure Slimstat to track specific file extensions as downloads.', 'wp-slimstat' )
617
+ ),
618
+ 'slim_p4_10' => array(
619
+ 'title' => __( 'Recent Events', 'wp-slimstat' ),
620
+ 'callback' => array( __CLASS__, 'show_events' ),
621
+ 'callback_args' => array(
622
+ 'type' => 'recent',
623
+ 'columns' => 'notes',
624
+ 'raw' => array( 'wp_slimstat_db', 'get_recent_events' )
625
+ ),
626
+ 'classes' => array( 'normal', 'hidden' ),
627
+ 'screens' => array( 'slimview4' ),
628
+ 'tooltip' => __( 'This report lists any <em>event</em> occurred on your website. Please refer to the FAQ for more information on how to use this functionality.', 'wp-slimstat' )
629
+ ),
630
+ 'slim_p4_11' => array(
631
+ 'title' => __( 'Top Posts', 'wp-slimstat' ),
632
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
633
+ 'callback_args' => array(
634
+ 'type' => 'top',
635
+ 'columns' => 'resource',
636
+ 'where' => 'content_type = "post"',
637
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
638
+ ),
639
+ 'classes' => array( 'normal' ),
640
+ 'screens' => array( 'slimview4' )
641
+ ),
642
+ 'slim_p4_12' => array(
643
+ 'title' => __( 'Top Events', 'wp-slimstat' ),
644
+ 'callback' => array( __CLASS__, 'show_events' ),
645
+ 'callback_args' => array(
646
+ 'type' => 'top',
647
+ 'columns' => 'notes',
648
+ 'raw' => array( 'wp_slimstat_db', 'get_top_events' )
649
+ ),
650
+ 'classes' => array( 'normal', 'hidden' ),
651
+ 'screens' => array( 'slimview4' ),
652
+ 'tooltip' => __( 'This report lists any <em>event</em> occurred on your website. Please refer to the FAQ for more information on how to use this functionality.', 'wp-slimstat' )
653
+ ),
654
+ 'slim_p4_13' => array(
655
+ 'title' => __( 'Top Internal Searches', 'wp-slimstat' ),
656
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
657
+ 'callback_args' => array(
658
+ 'type' => 'top',
659
+ 'columns' => 'searchterms',
660
+ 'where' => 'content_type LIKE "%search%" AND searchterms <> "" AND searchterms IS NOT NULL',
661
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
662
+ ),
663
+ 'classes' => array( 'normal', 'hidden' ),
664
+ 'screens' => array( 'slimview4' )
665
+ ),
666
+ 'slim_p4_15' => array(
667
+ 'title' => __( 'Recent Categories', 'wp-slimstat' ),
668
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
669
+ 'callback_args' => array(
670
+ 'type' => 'recent',
671
+ 'columns' => 'resource',
672
+ 'where' => '(content_type = "category" OR content_type = "tag")',
673
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
674
+ ),
675
+ 'classes' => array( 'normal', 'hidden' ),
676
+ 'screens' => array( 'slimview4' )
677
+ ),
678
+ 'slim_p4_16' => array(
679
+ 'title' => __( 'Top Pages Not Found', 'wp-slimstat' ),
680
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
681
+ 'callback_args' => array(
682
+ 'type' => 'top',
683
+ 'columns' => 'resource',
684
+ 'where' => 'content_type LIKE "%404%"',
685
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
686
+ ),
687
+ 'classes' => array( 'normal' ),
688
+ 'screens' => array( 'slimview4' )
689
+ ),
690
+ 'slim_p4_18' => array(
691
+ 'title' => __( 'Top Authors', 'wp-slimstat' ),
692
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
693
+ 'callback_args' => array(
694
+ 'type' => 'top',
695
+ 'columns' => 'author',
696
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
697
+ ),
698
+ 'classes' => array( 'normal' ),
699
+ 'screens' => array( 'slimview4', 'dashboard' )
700
+ ),
701
+ 'slim_p4_19' => array(
702
+ 'title' => __( 'Top Tags', 'wp-slimstat' ),
703
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
704
+ 'callback_args' => array(
705
+ 'type' => 'top',
706
+ 'columns' => 'category',
707
+ 'where' => '(content_type LIKE "%tag%")',
708
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
709
+ ),
710
+ 'classes' => array( 'normal', 'hidden' ),
711
+ 'screens' => array( 'slimview4' )
712
+ ),
713
+ 'slim_p4_20' => array(
714
+ 'title' => __( 'Recent Downloads', 'wp-slimstat' ),
715
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
716
+ 'callback_args' => array(
717
+ 'type' => 'recent',
718
+ 'columns' => 'resource',
719
+ 'where' => 'content_type = "download"',
720
+ 'raw' => array( 'wp_slimstat_db', 'get_recent' )
721
+ ),
722
+ 'classes' => array( 'large', 'hidden' ),
723
+ 'screens' => array( 'slimview4' )
724
+ ),
725
+ 'slim_p4_21' => array(
726
+ 'title' => __( 'Top Outbound Links', 'wp-slimstat' ),
727
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
728
+ 'callback_args' => array(
729
+ 'type' => 'top',
730
+ 'columns' => 'outbound_resource',
731
+ 'raw' => array( 'wp_slimstat_db', 'get_top_outbound' ),
732
+ 'criteria' => 'swap'
733
+ ),
734
+ 'classes' => array( 'normal', 'hidden' ),
735
+ 'screens' => array( 'slimview4', 'dashboard' ),
736
+ ),
737
+ 'slim_p4_22' => array(
738
+ 'title' => __( 'Your Website', 'wp-slimstat' ),
739
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
740
+ 'callback_args' => array(
741
+ 'raw' => array( __CLASS__, 'get_your_blog' )
742
+ ),
743
+ 'classes' => array( 'normal', 'hidden' ),
744
+ 'screens' => array( 'slimview4' ),
745
+ 'tooltip' => __( 'Your content at a glance: posts, comments, pingbacks, etc. Please note that this report is not affected by the filters set here above.', 'wp-slimstat' )
746
+ ),
747
+ 'slim_p4_23' => array(
748
+ 'title' => __( 'Top Bounce Pages', 'wp-slimstat' ),
749
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
750
+ 'callback_args' => array(
751
+ 'type' => 'top',
752
+ 'columns' => 'resource',
753
+ 'where' => 'content_type <> "404"',
754
+ 'having' => 'HAVING COUNT(visit_id) = 1',
755
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
756
+ ),
757
+ 'classes' => array( 'normal', 'hidden' ),
758
+ 'screens' => array( 'slimview4' )
759
+ ),
760
+ 'slim_p4_24' => array(
761
+ 'title' => __( 'Top Exit Pages', 'wp-slimstat' ),
762
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
763
+ 'callback_args' => array(
764
+ 'type' => 'top',
765
+ 'columns' => 'visit_id',
766
+ 'outer_select_column' => 'resource',
767
+ 'aggr_function' => 'MAX',
768
+ 'raw' => array( 'wp_slimstat_db', 'get_top_aggr' )
769
+ ),
770
+ 'classes' => array( 'large', 'hidden' ),
771
+ 'screens' => array( 'slimview4', 'dashboard' )
772
+ ),
773
+ 'slim_p4_25' => array(
774
+ 'title' => __( 'Top Entry Pages', 'wp-slimstat' ),
775
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
776
+ 'callback_args' => array(
777
+ 'type' => 'top',
778
+ 'columns' => 'visit_id',
779
+ 'outer_select_column' => 'resource',
780
+ 'aggr_function' => 'MIN',
781
+ 'raw' => array( 'wp_slimstat_db', 'get_top_aggr' )
782
+ ),
783
+ 'classes' => array( 'large', 'hidden' ),
784
+ 'screens' => array( 'slimview4' )
785
+ ),
786
+ 'slim_p4_26_01' => array( // Chart Reports need to always have a _01 suffix to tell our custom "refresh" code to avoid fading the chart, which apparently doesn't work
787
+ 'title' => __( 'Pages with Outbound Links', 'wp-slimstat' ),
788
+ 'callback' => array( __CLASS__, 'show_chart' ),
789
+ 'callback_args' => array(
790
+ 'id' => 'slim_p4_26_01',
791
+ 'chart_data' => array(
792
+ 'data1' => 'COUNT( outbound_resource )',
793
+ 'data2' => 'COUNT( DISTINCT outbound_resource )'
794
+ ),
795
+ 'chart_labels' => array(
796
+ __( 'Outbound Links', 'wp-slimstat' ),
797
+ __( 'Unique Outbound', 'wp-slimstat' )
798
+ )
799
+ ),
800
+ 'classes' => array( 'extralarge', 'chart' ),
801
+ 'screens' => array( 'slimview4' ),
802
+ 'tooltip' => $chart_tooltip
803
+ ),
804
+
805
+ 'slim_p6_01' => array(
806
+ 'title' => __( 'World Map', 'wp-slimstat' ),
807
+ 'callback' => array( __CLASS__, 'show_world_map' ),
808
+ 'callback_args' => array(
809
+ 'id' => 'slim_p6_01'
810
+ ),
811
+ 'classes' => array( 'full-width', 'tall' ),
812
+ 'screens' => array( 'slimview6' ),
813
+ 'tooltip' => __( 'Dots on the map represent the most recent pageviews geolocated by City. This feature is only available by enabling the corresponding precision level in the settings.', 'wp-slimstat' )
814
+ )
815
+ );
816
+
817
+ if ( wp_slimstat::$settings[ 'geolocation_country' ] != 'on' ) {
818
+ self::$reports_info [ 'slim_p2_23' ] = array(
819
+ 'title' => __( 'Top Cities', 'wp-slimstat' ),
820
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
821
+ 'callback_args' => array(
822
+ 'type' => 'top',
823
+ 'columns' => 'city',
824
+
825
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
826
+ ),
827
+ 'classes' => array( 'normal' ),
828
+ 'screens' => array( 'slimview3' )
829
+ );
830
+ }
831
+
832
+ // Allow third party tools to manipulate this list here above: please use unique report IDs that don't interfere with built-in ones, if you add your own custom report
833
+ self::$reports_info = apply_filters( 'slimstat_reports_info', self::$reports_info );
834
+
835
+ // Define what reports have been deprecated over time, to remove them from the user's settings
836
+ $deprecated_reports = array(
837
+ 'slim_p1_05' => 1,
838
+ 'slim_p1_18' => 1,
839
+ 'slim_p2_10' => 1,
840
+ 'slim_p3_03' => 1,
841
+ 'slim_p3_04' => 1,
842
+ 'slim_p3_05' => 1,
843
+ 'slim_p3_08' => 1,
844
+ 'slim_p3_09' => 1,
845
+ 'slim_p3_10' => 1,
846
+ 'slim_p4_08' => 1,
847
+ 'slim_p4_14' => 1,
848
+ 'slim_p4_17' => 1,
849
+ 'slim_getsocial' => 1
850
+ );
851
+
852
+ // Retrieve this user's list of active reports,
853
+ $current_user = wp_get_current_user();
854
+ $page_location = ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'on' ) ? 'slimstat' : 'admin';
855
+
856
+ // Superadmins can customize the layout at network level, to override per-site settings
857
+ self::$user_reports = get_user_option( "meta-box-order_slimstat_page_slimlayout-network", 1 );
858
+
859
+ // No network-wide settings exist
860
+ if ( empty( self::$user_reports ) ) {
861
+ self::$user_reports = get_user_option( "meta-box-order_slimstat_page_slimlayout", $current_user->ID );
862
+ }
863
+
864
+ // Do this only if we are in one of our screens (no dashboard!)
865
+ if ( is_admin() && !empty( $_REQUEST[ 'page' ] ) && strpos( $_REQUEST[ 'page' ], 'slimview' ) !== false ) {
866
+
867
+ // If this list is not empty, we rearrange the order of our reports
868
+ if ( !empty( self::$user_reports[ $_REQUEST[ 'page' ] ] ) ) {
869
+ $user_reports_intersect = array_flip( explode( ',', self::$user_reports[ $_REQUEST[ 'page' ] ] ) );
870
+ self::$reports_info = array_intersect_key( array_merge( $user_reports_intersect, self::$reports_info ), $user_reports_intersect );
871
+ }
872
+ else {
873
+ foreach ( self::$reports_info as $a_report_id => $a_report_info ) {
874
+ if ( !in_array( $_REQUEST[ 'page' ], $a_report_info[ 'screens' ] ) ) {
875
+ unset( self::$reports_info[ $a_report_id ] );
876
+ }
877
+ }
878
+ }
879
+
880
+ // Remove deprecated reports
881
+ self::$reports_info = array_diff_key( self::$reports_info, $deprecated_reports );
882
+
883
+ $hidden_reports = get_user_option( "metaboxhidden_{$page_location}_page_{$_REQUEST['page']}", $current_user->ID );
884
+
885
+ // If this list is not empty, use it instead of the predefined visibility
886
+ if ( is_array( $hidden_reports ) ) {
887
+ foreach ( self::$reports_info as $a_report_id => $a_report_info ) {
888
+ if ( in_array( $a_report_id, $hidden_reports ) ) {
889
+ if ( is_array( self::$reports_info[ $a_report_id ][ 'classes' ] ) && !in_array( 'hidden', $a_report_info[ 'classes' ] ) ) {
890
+ self::$reports_info[ $a_report_id ][ 'classes' ][] = 'hidden';
891
+ }
892
+ }
893
+ else if ( is_array( self::$reports_info[ $a_report_id ][ 'classes' ] ) ) {
894
+ self::$reports_info[ $a_report_id ][ 'classes' ] = array_diff( self::$reports_info[ $a_report_id ][ 'classes' ], array( 'hidden' ) );
895
+ }
896
+ }
897
+ }
898
+ }
899
+ // If we are on the WP Dashboard page, all the reports are 'visible': WP will take care of honoring the Screen Options settings for that page
900
+ else if ( !empty( $_REQUEST['page'] ) && strpos( $_REQUEST['page'], 'slimlayout' ) === false ) {
901
+ foreach ( self::$reports_info as $a_report_id => $a_report_info ) {
902
+ if ( is_array( self::$reports_info[ $a_report_id ][ 'classes' ] ) ) {
903
+ self::$reports_info[ $a_report_id ][ 'classes' ] = array_diff( self::$reports_info[ $a_report_id ][ 'classes' ], array( 'hidden' ) );
904
+ }
905
+ }
906
+ }
907
+ }
908
+ // end init
909
+
910
+ public static function report_header( $_report_id = '' ) {
911
+ if ( empty( self::$reports_info[ $_report_id ] ) ) {
912
+ return false;
913
+ }
914
+
915
+ $header_classes = !empty( self::$reports_info[ $_report_id ][ 'classes' ] ) ? implode( ' ', self::$reports_info[ $_report_id ][ 'classes' ] ) : '';
916
+ $header_buttons = '';
917
+ $header_tooltip = '';
918
+
919
+ // Don't show the header buttons on the frontend
920
+ if ( is_admin() ) {
921
+ // Show the refresh button only if the time range is not in the past
922
+ if ( wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] >= date_i18n( 'U' ) - 300 ) {
923
+ $header_buttons = '<a class="noslimstat refresh slimstat-font-arrows-cw" title="'.__('Refresh','wp-slimstat').'" href="'.self::fs_url().'"></a>';
924
+ }
925
+
926
+ // Allow third-party code to add more buttons
927
+ $header_buttons = apply_filters( 'slimstat_report_header_buttons', $header_buttons, $_report_id );
928
+ $header_buttons = '<div class="slimstat-header-buttons">' . $header_buttons . '</div>';
929
+ $header_tooltip = !empty( self::$reports_info[ $_report_id ][ 'tooltip' ] ) ? '<i class="slimstat-tooltip-trigger corner"><span class="slimstat-tooltip-content">' . self::$reports_info[ $_report_id ][ 'tooltip' ] . '</span></i>' : '';
930
+ }
931
+
932
+ echo "<div class='postbox $header_classes' id='$_report_id'>{$header_buttons} <h3 data-report-id='{$_report_id}'>" . self::$reports_info[ $_report_id ][ 'title' ] . " {$header_tooltip}</h3><div class='inside'>";
933
+ }
934
+
935
+ public static function report_footer(){
936
+ echo '</div></div>';
937
+ }
938
+
939
+ public static function report_pagination( $_count_page_results = 0, $_count_all_results = 0, $_show_refresh_countdown = false, $_results_per_page = -1 ) {
940
+ if ( !is_admin() ) {
941
+ return '';
942
+ }
943
+
944
+ $_results_per_page = ( $_results_per_page < 0 ) ? wp_slimstat::$settings[ 'rows_to_show' ] : $_results_per_page;
945
+
946
+ $endpoint = min( $_count_all_results, wp_slimstat_db::$filters_normalized[ 'misc' ][ 'start_from' ] + $_results_per_page );
947
+ $pagination_buttons = '';
948
+ $direction_prev = is_rtl() ? 'right' : 'left';
949
+ $direction_next = is_rtl() ? 'left' : 'right';
950
+
951
+ if ( $endpoint + $_results_per_page < $_count_all_results && $_count_page_results > 0 ) {
952
+ $startpoint = $_count_all_results - $_count_all_results % $_results_per_page;
953
+ if ( $startpoint == $_count_all_results ) {
954
+ $startpoint -= $_results_per_page;
955
+ }
956
+ $pagination_buttons .= '<a class="refresh slimstat-font-angle-double-' . $direction_next . '" href="' . wp_slimstat_reports::fs_url( 'start_from equals ' . $startpoint ) . '"></a> ';
957
+ }
958
+ if ($endpoint < $_count_all_results && $_count_page_results > 0){
959
+ $startpoint = wp_slimstat_db::$filters_normalized['misc']['start_from'] + $_results_per_page;
960
+ $pagination_buttons .= '<a class="refresh slimstat-font-angle-'.$direction_next.'" href="' . wp_slimstat_reports::fs_url( 'start_from equals ' . $startpoint ) . '"></a> ';
961
+ }
962
+ if (wp_slimstat_db::$filters_normalized['misc']['start_from'] > 0){
963
+ $startpoint = (wp_slimstat_db::$filters_normalized['misc']['start_from'] > $_results_per_page)?wp_slimstat_db::$filters_normalized['misc']['start_from'] - $_results_per_page : 0;
964
+ $pagination_buttons .= '<a class="refresh slimstat-font-angle-'.$direction_prev.'" href="'.wp_slimstat_reports::fs_url('start_from equals '.$startpoint).'"></a> ';
965
+ }
966
+ if (wp_slimstat_db::$filters_normalized['misc']['start_from'] - $_results_per_page > 0){
967
+ $pagination_buttons .= '<a class="refresh slimstat-font-angle-double-'.$direction_prev.'" href="'.wp_slimstat_reports::fs_url('start_from equals 0').'"></a> ';
968
+ }
969
+
970
+ $pagination = '<p class="pagination">' . sprintf( __( 'Results %s - %s of %s', 'wp-slimstat' ), number_format( wp_slimstat_db::$filters_normalized[ 'misc' ][ 'start_from' ] + 1, 0, '', wp_slimstat_db::$formats[ 'thousand' ] ), number_format( $endpoint, 0, '', wp_slimstat_db::$formats[ 'thousand' ] ), number_format( $_count_all_results, 0, '', wp_slimstat_db::$formats[ 'thousand' ] ) . ( ( $_count_all_results == wp_slimstat::$settings[ 'limit_results' ] ) ? '+' : '') );
971
+
972
+ if ( $_show_refresh_countdown && wp_slimstat::$settings[ 'refresh_interval' ] > 0 && wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] >= date_i18n( 'U' ) - 300 ) {
973
+ $pagination .= ' [' . __( 'Refresh in', 'wp-slimstat' ) . ' <i class="refresh-timer"></i>]';
974
+ }
975
+ $pagination .= $pagination_buttons.'</p>';
976
+
977
+ return $pagination;
978
+ }
979
+
980
+ public static function callback_wrapper() {
981
+ $_args = self::_check_args( func_get_args() );
982
+ call_user_func( $_args[ 'callback' ] , $_args[ 'callback_args' ] );
983
+ }
984
+
985
+ public static function raw_results_to_html( $_args = array() ) {
986
+ if ( wp_slimstat::$settings[ 'async_load' ] == 'on' && ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
987
+ return '';
988
+ }
989
+
990
+ wp_slimstat_db::$debug_message = '';
991
+
992
+ $all_results = call_user_func( $_args[ 'raw' ] , $_args );
993
+
994
+ echo wp_slimstat_db::$debug_message;
995
+
996
+ // Some reports don't need any kind of pre/post-processing, we just display the data contained in the array
997
+ if ( empty( $_args[ 'columns' ] ) ) {
998
+ foreach ( $all_results as $a_result ) {
999
+ echo '<p>';
1000
+
1001
+ if ( !empty( $a_result[ 'tooltip' ] ) ) {
1002
+ self::inline_help( $a_result[ 'tooltip' ] );
1003
+ }
1004
+
1005
+ echo "{$a_result[ 'metric' ]} <span>{$a_result[ 'value' ]}</span>";
1006
+
1007
+ if ( !empty( $a_result[ 'details' ] ) ) {
1008
+ $is_expanded = ( wp_slimstat::$settings[ 'expand_details' ] == 'on' ) ? ' expanded' : '';
1009
+ echo "<b class='slimstat-tooltip-content$is_expanded'>{$a_result[ 'details' ]}</b>";
1010
+ }
1011
+
1012
+ echo '</p>';
1013
+ }
1014
+ }
1015
+ else {
1016
+ $results = array_slice(
1017
+ $all_results,
1018
+ wp_slimstat_db::$filters_normalized[ 'misc' ][ 'start_from' ],
1019
+ wp_slimstat::$settings[ 'rows_to_show' ]
1020
+ );
1021
+
1022
+ // Count the results
1023
+ $count_page_results = count( $results );
1024
+
1025
+ if ($count_page_results == 0){
1026
+ echo '<p class="nodata">' . __( 'No data to display', 'wp-slimstat' ) . '</p>';
1027
+
1028
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1029
+ die();
1030
+ }
1031
+ else{
1032
+ return array();
1033
+ }
1034
+ }
1035
+
1036
+ // Some reports use aliases for column names
1037
+ if ( !empty( $_args[ 'as_column' ] ) ) {
1038
+ $_args[ 'columns' ] = $_args[ 'as_column' ];
1039
+ }
1040
+ else if ( !empty( $_args[ 'outer_select_column' ] ) ) {
1041
+ $_args[ 'columns' ] = $_args[ 'outer_select_column' ];
1042
+ }
1043
+
1044
+ // Some reports query more than one column
1045
+ if ( strpos( $_args[ 'columns' ], ',' ) !== false ) {
1046
+ $_args[ 'columns' ] = explode( ',', $_args[ 'columns' ] );
1047
+ $_args[ 'columns' ] = trim( $_args[ 'columns' ][ 0 ] );
1048
+ }
1049
+
1050
+ echo self::report_pagination( $count_page_results, count( $all_results ) );
1051
+
1052
+ $is_expanded = ( is_admin() && wp_slimstat::$settings[ 'expand_details' ] == 'on' ) ? ' expanded' : '';
1053
+ $permalinks_enabled = get_option( 'permalink_structure' );
1054
+ $column_not_calculated = str_replace( '_calculated', '', $_args[ 'columns' ] );
1055
+
1056
+ for($i=0; $i<$count_page_results; $i++){
1057
+ $row_details = $percentage = '';
1058
+ $element_pre_value = '';
1059
+ $element_value = $results[ $i ][ $_args[ 'columns' ] ];
1060
+
1061
+ // Some columns require a special pre-treatment
1062
+ switch ( $column_not_calculated ){
1063
+
1064
+ case 'browser':
1065
+ if ( !empty( $results[ $i ][ 'user_agent' ] ) && wp_slimstat::$settings[ 'show_complete_user_agent_tooltip' ] == 'on' ) {
1066
+ $element_pre_value = self::inline_help($results[$i]['user_agent'], false);
1067
+ }
1068
+ $element_value = $results[$i]['browser'].((isset($results[$i]['browser_version']) && intval($results[$i]['browser_version']) != 0)?' '.$results[$i]['browser_version']:'');
1069
+ break;
1070
+
1071
+ case 'category':
1072
+ $row_details = __( 'Category ID', 'wp-slimstat' ) . ": {$results[ $i ][ $_args[ 'columns' ] ]}";
1073
+ $element_value = get_cat_name( $results[ $i ][ $_args[ 'columns' ] ] );
1074
+ break;
1075
+
1076
+ case 'country':
1077
+ $row_details .= __('Code','wp-slimstat').": {$results[$i]['country']}";
1078
+ $element_value = __('c-'.$results[$i]['country'], 'wp-slimstat');
1079
+ break;
1080
+
1081
+ case 'ip':
1082
+ if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'on' ) {
1083
+ $element_value = gethostbyaddr( $results[ $i ][ $_args[ 'columns' ] ] );
1084
+ }
1085
+ else{
1086
+ $element_value = $results[ $i ][ $_args[ 'columns' ] ];
1087
+ }
1088
+ break;
1089
+
1090
+ case 'language':
1091
+ $row_details = __( 'Code', 'wp-slimstat' ) . ": {$results[ $i ][ $_args[ 'columns' ] ]}";
1092
+ $element_value = __( 'l-' . $results[ $i ][ $_args[ 'columns' ] ], 'wp-slimstat' );
1093
+ break;
1094
+
1095
+ case 'platform':
1096
+ $row_details = __( 'Code', 'wp-slimstat' ).": {$results[ $i ][ $_args[ 'columns' ] ]}";
1097
+ $element_value = __( $results[ $i ][ $_args[ 'columns' ] ], 'wp-slimstat' );
1098
+ $results[ $i ][ $_args[ 'columns' ] ] = str_replace( 'p-', '', $results[ $i ][ $_args[ 'columns' ] ] );
1099
+ break;
1100
+
1101
+ case 'referer':
1102
+ $element_value = str_replace( array( '<', '>' ), array( '&lt;', '&gt;' ), urldecode( $results[ $i ][ $_args[ 'columns' ] ] ) );
1103
+ //$element_value = parse_url( $element_value, PHP_URL_HOST );
1104
+ break;
1105
+
1106
+ case 'resource':
1107
+ $resource_title = self::get_resource_title( $results[ $i ][ $_args[ 'columns' ] ] );
1108
+ if ( $resource_title != $results[ $i ][ $_args[ 'columns' ] ] ) {
1109
+ $row_details = __( 'URL', 'wp-slimstat' ) . ': ' . htmlentities( $results[ $i ][ $_args[ 'columns' ] ], ENT_QUOTES, 'UTF-8' );
1110
+ }
1111
+ if ( !empty( $_args[ 'where' ] ) && strpos( $_args[ 'where' ], 'download' ) !== false ) {
1112
+ $clean_extension = pathinfo( strtolower( parse_url( $results[ $i ][ $_args[ 'columns' ] ], PHP_URL_PATH ) ), PATHINFO_EXTENSION );
1113
+ if ( in_array( $clean_extension, array( 'jpg', 'gif', 'png', 'jpeg', 'bmp' ) ) ) {
1114
+ $row_details = '<br><img src="' . $results[ $i ][ $_args[ 'columns' ] ] . '" style="width:100px">';
1115
+ }
1116
+ }
1117
+ $element_value = $resource_title;
1118
+ break;
1119
+
1120
+ case 'screen_width':
1121
+ $element_value = "{$results[ $i ][ $_args[ 'columns' ] ]} x {$results[ $i ][ 'screen_height' ]}";
1122
+ break;
1123
+
1124
+ case 'searchterms':
1125
+ if ( $_args[ 'type' ] == 'recent' ) {
1126
+ $domain = parse_url( $results[ $i ][ 'referer' ], PHP_URL_HOST );
1127
+
1128
+ $row_details = __( 'Referrer', 'wp-slimstat' ) . ": $domain";
1129
+ $element_value = self::get_search_terms_info( $results[ $i ][ 'searchterms' ], $results[ $i ][ 'referer' ], true );
1130
+ }
1131
+ else{
1132
+ $element_value = htmlentities( $results[ $i ][ 'searchterms' ], ENT_QUOTES, 'UTF-8' );
1133
+ }
1134
+ break;
1135
+
1136
+ case 'username':
1137
+ $element_value = $results[ $i ][ 'username' ];
1138
+ if ( wp_slimstat::$settings[ 'show_display_name' ] == 'on' ) {
1139
+ $element_custom_value = get_user_by( 'login', $results[ $i ][ 'username' ] );
1140
+ if ( is_object( $element_custom_value ) ) {
1141
+ $element_value = $element_custom_value->display_name;
1142
+ }
1143
+ }
1144
+ break;
1145
+
1146
+ case 'visit_id':
1147
+ $resource_title = self::get_resource_title( $results[ $i ][ 'resource' ] );
1148
+ if ( $resource_title != $results[ $i ][ 'resource' ] ) {
1149
+ $row_details = htmlentities( $results[ $i ][ 'resource' ], ENT_QUOTES, 'UTF-8' );
1150
+ }
1151
+ $element_value = $resource_title;
1152
+ break;
1153
+ default:
1154
+ }
1155
+
1156
+ if ( is_admin() ) {
1157
+ $element_value = "<a class='slimstat-filter-link' href='" . self::fs_url( $column_not_calculated. ' ' . $_args[ 'filter_op' ] . ' ' . $results[ $i ][ $_args[ 'columns' ] ] ) . "'>$element_value</a>";
1158
+ }
1159
+
1160
+ if ( !empty( $_args['type'] ) && $_args['type'] == 'recent' ) {
1161
+ $row_details = date_i18n(wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ], $results[ $i ][ 'dt' ], true ) . ( !empty( $row_details ) ? '<br>' : '' ) . $row_details;
1162
+ }
1163
+
1164
+ if ( !empty($_args[ 'type' ] ) && $_args[ 'type' ] == 'top' ) {
1165
+ $percentage_value = ( ( self::$pageviews > 0 ) ? number_format( sprintf( "%01.2f", ( 100 * $results[ $i ][ 'counthits' ] / self::$pageviews ) ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 );
1166
+ $counthits = number_format( $results[ $i ][ 'counthits' ], 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1167
+
1168
+ if ( !empty( $_args[ 'criteria' ] ) && $_args[ 'criteria' ] == 'swap' ) {
1169
+ $percentage = ' <span>' . $counthits . '</span>';
1170
+ $row_details = __('Hits','wp-slimstat') . ': ' . ( ( $column_not_calculated != 'outbound_resource' ) ? $percentage_value . '%' . ( !empty( $row_details ) ? '<br>' : '' ) . $row_details : '' );
1171
+ }
1172
+ else {
1173
+ $percentage = ' <span>' . $percentage_value . '%</span>';
1174
+ $row_details = __('Hits','wp-slimstat') . ': ' . $counthits . ( !empty( $row_details ) ? '<br>' : '' ) . $row_details;
1175
+ }
1176
+ }
1177
+
1178
+ // Some columns require a special post-treatment
1179
+ if ( $_args[ 'columns' ] == 'resource' && strpos( $_args['where'], '404' ) === false ) {
1180
+ $base_url = '';
1181
+ if (isset($results[$i]['blog_id'])){
1182
+ $base_url = parse_url(get_site_url($results[$i]['blog_id']));
1183
+ $base_url = $base_url['scheme'].'://'.$base_url['host'];
1184
+ }
1185
+ $element_value = '<a target="_blank" class="slimstat-font-logout" title="'.__('Open this URL in a new window','wp-slimstat').'" href="'.$base_url.htmlentities($results[$i]['resource'], ENT_QUOTES, 'UTF-8').'"></a> '.$base_url.$element_value;
1186
+ }
1187
+
1188
+ if ( $_args[ 'columns' ] == 'referer_calculated' && !empty( $_args[ 'type' ] ) && $_args[ 'type' ] == 'top' ) {
1189
+ $element_url = 'http://' . htmlentities( $results[ $i ][ 'referer_calculated' ], ENT_QUOTES, 'UTF-8' );
1190
+ $element_value = '<a target="_blank" class="slimstat-font-logout" title="'.__('Open this URL in a new window','wp-slimstat').'" href="'.$element_url.'"></a> '.$element_value;
1191
+ }
1192
+
1193
+ if ( $_args[ 'columns' ] == 'referer' && !empty( $_args[ 'type' ] ) && $_args[ 'type' ] == 'top' ) {
1194
+ $element_url = htmlentities( $results[ $i ][ 'referer' ], ENT_QUOTES, 'UTF-8' );
1195
+ $element_value = '<a target="_blank" class="slimstat-font-logout" title="'.__('Open this URL in a new window','wp-slimstat').'" href="'.$element_url.'"></a> '.$element_value;
1196
+ }
1197
+
1198
+ if ( is_admin() && !empty( $results[ $i ][ 'ip' ]) && $_args[ 'columns' ] != 'ip' && wp_slimstat::$settings[ 'convert_ip_addresses' ] != 'on' ) {
1199
+ $row_details .= '<br> IP: <a class="slimstat-filter-link" href="'.self::fs_url( 'ip equals ' . $results[ $i ][ 'ip' ] ) . '">' . $results[ $i ][ 'ip' ] . '</a>' . ( !empty( $results[ $i ][ 'other_ip' ] ) ? ' / ' . $results[ $i ][ 'other_ip' ] : '' ) . '<a title="WHOIS: ' . $results[ $i ][ 'ip' ] . '" class="slimstat-font-location-1 whois" href="' . wp_slimstat::$settings[ 'ip_lookup_service' ] . $results[ $i ][ 'ip' ] . '"></a>';
1200
+ }
1201
+ if ( !empty( $row_details ) ) {
1202
+ $row_details = "<b class='slimstat-tooltip-content$is_expanded'>$row_details</b>";
1203
+ }
1204
+
1205
+ $row_output = "<p class='slimstat-tooltip-trigger'>$element_pre_value$element_value$percentage $row_details</p>";
1206
+
1207
+ // Strip all the filter links, if this information is shown on the frontend
1208
+ if ( !is_admin() ) {
1209
+ $row_output = preg_replace('/<a (.*?)>(.*?)<\/a>/', "\\2", $row_output);
1210
+ }
1211
+
1212
+ echo $row_output;
1213
+ }
1214
+ }
1215
+
1216
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1217
+ die();
1218
+ }
1219
+ }
1220
+
1221
+ public static function show_activity_log( $_args = array() ) {
1222
+ // This function is too long, so it was moved to a separate file
1223
+ include( WP_PLUGIN_DIR . '/wp-slimstat/admin/view/right-now.php' );
1224
+
1225
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1226
+ die();
1227
+ }
1228
+ }
1229
+
1230
+ public static function show_chart( $_args = array() ) {
1231
+ $data = wp_slimstat_db::get_data_for_chart( $_args[ 'chart_data' ] );
1232
+
1233
+ if ( empty( $data[ 'keys' ] ) ) {
1234
+ echo '<p class="nodata">' . __( 'No data to display', 'wp-slimstat') . '</p>';
1235
+
1236
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1237
+ die();
1238
+ }
1239
+ else {
1240
+ return 0;
1241
+ }
1242
+ }
1243
+
1244
+ // Enqueue all the Javascript and styles
1245
+ $path_slimstat = dirname( dirname( __FILE__ ) );
1246
+ wp_enqueue_script( 'slimstat_amcharts', plugins_url( '/admin/js/amcharts/amcharts.js', $path_slimstat ), array(), null, false );
1247
+ wp_enqueue_script( 'slimstat_amcharts_serial', plugins_url( '/admin/js/amcharts/serial.js', $path_slimstat ), array( 'slimstat_amcharts' ), null, false );
1248
+ wp_enqueue_script( 'slimstat_amcharts_plugins_export', plugins_url( '/admin/js/amcharts/plugins/export/export.min.js', $path_slimstat ), array( 'slimstat_amcharts' ), null, false );
1249
+ wp_enqueue_script( 'slimstat_amcharts_theme_light', plugins_url( '/admin/js/amcharts/themes/light.js', $path_slimstat ), array( 'slimstat_amcharts' ), null, false );
1250
+
1251
+ wp_enqueue_style( 'slimstat_amcharts_plugins_export_css', plugins_url( '/admin/js/amcharts/plugins/export/export.css', $path_slimstat ) );
1252
+
1253
+ $chart_colors = !empty( wp_slimstat::$settings[ 'chart_colors' ] ) ? wp_slimstat::string_to_array( wp_slimstat::$settings[ 'chart_colors' ] ) : array( '#bbcc44', '#21759b', '#ccc', '#999' );
1254
+
1255
+ ?>
1256
+ <div class="chart-placeholder" id="chart_<?php echo $_args[ 'id' ]; ?>" style="min-height: 280px"></div>
1257
+
1258
+ <script type="text/javascript">
1259
+ <?php if ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ): ?>
1260
+ jQuery(function() {
1261
+ <?php endif; ?>
1262
+ var chart_<?php echo $_args[ 'id' ]; ?> = AmCharts.makeChart( "chart_<?php echo $_args[ 'id' ]; ?>", {
1263
+ "type": "serial",
1264
+ "zoomOutButtonPadding": 25,
1265
+ "theme": "light",
1266
+ "creditsPosition": "top-left",
1267
+ "mouseWheelZoomEnabled": true,
1268
+ "legend": {
1269
+ "equalWidths": true,
1270
+ "position": "bottom",
1271
+ "align": "center"
1272
+ },
1273
+ "valueAxes": [ {
1274
+ "id":"v1",
1275
+ "axisAlpha": 0,
1276
+ "integersOnly": true,
1277
+ "position": "left",
1278
+ } ],
1279
+ "chartCursor": {
1280
+ "cursorPosition": "mouse",
1281
+ "pan": true,
1282
+ "valueLineBalloonEnabled": true
1283
+ },
1284
+ "categoryField": "v1_label",
1285
+ "categoryAxis": {
1286
+ "dashLength": 1,
1287
+ "minHorizontalGap": <?php echo ( !empty( $data[ 'keys' ] ) && strlen( $data[ 0 ][ 'v1_label' ] ) > 5 ) ? 150 : 100; ?>,
1288
+ "parseDates": false,
1289
+ "position": "bottom"
1290
+ },
1291
+ "export": {
1292
+ "enabled": true
1293
+ },
1294
+ "numberFormatter": {
1295
+ "precision": 0
1296
+ },
1297
+ "graphs": [
1298
+ <?php if ( wp_slimstat::$settings[ 'comparison_chart' ] == 'on' ): ?>
1299
+ {
1300
+ "id": "g3_<?php echo $_args[ 'id' ]; ?>",
1301
+ "balloonText": "[[v3_label]]: [[value]]",
1302
+ "bullet": "round",
1303
+ "bulletBorderAlpha": 1,
1304
+ "bulletColor": "#00FF00",
1305
+ "bulletSize": 4,
1306
+ "hideBulletsCount": 50,
1307
+ "lineThickness": 2,
1308
+ "lineColor": "<?php echo $chart_colors[ 2 ] ?>",
1309
+ "title": "<?php echo htmlspecialchars( $_args[ 'chart_labels' ][ 0 ], ENT_QUOTES, 'UTF-8' ) . ' ' . __( '(previous)', 'wp-slimstat' ); ?>",
1310
+ "type": "line",
1311
+ "useLineColorForBulletBorder": true,
1312
+ "valueField": "v3"
1313
+ }, {
1314
+ "id": "g4_<?php echo $_args[ 'id' ]; ?>",
1315
+ "balloonText": "[[v3_label]]: [[value]]",
1316
+ "bullet": "round",
1317
+ "bulletBorderAlpha": 1,
1318
+ "bulletColor": "#00FF00",
1319
+ "bulletSize": 4,
1320
+ "hideBulletsCount": 50,
1321
+ "lineThickness": 2,
1322
+ "lineColor": "<?php echo $chart_colors[ 3 ] ?>",
1323
+ "title": "<?php echo htmlspecialchars( $_args[ 'chart_labels' ][ 1 ], ENT_QUOTES, 'UTF-8' ) . ' ' . __( '(previous)', 'wp-slimstat' ); ?>",
1324
+ "type": "line",
1325
+ "useLineColorForBulletBorder": true,
1326
+ "valueField": "v4"
1327
+ },
1328
+ <?php endif; ?>
1329
+ {
1330
+ "id": "g1_<?php echo $_args[ 'id' ]; ?>",
1331
+ "balloonText": "<?php echo ( wp_slimstat::$settings[ 'comparison_chart' ] == 'on' ) ? '[[v1_label]]: [[value]]' : '[[value]]'; ?>",
1332
+ "bullet": "round",
1333
+ "bulletBorderAlpha": 1,
1334
+ "bulletColor": "#FFFFFF",
1335
+ "bulletSize": 4,
1336
+ "hideBulletsCount": 50,
1337
+ "lineColor": "<?php echo $chart_colors[ 0 ] ?>",
1338
+ "lineThickness": 2,
1339
+ "title": "<?php echo htmlspecialchars( $_args[ 'chart_labels' ][ 0 ], ENT_QUOTES, 'UTF-8' ); ?>",
1340
+ "type": "line",
1341
+ "useLineColorForBulletBorder": true,
1342
+ "valueField": "v1"
1343
+ }, {
1344
+ "id": "g2_<?php echo $_args[ 'id' ]; ?>",
1345
+ "balloonText": "<?php echo ( wp_slimstat::$settings[ 'comparison_chart' ] == 'on' ) ? '[[v1_label]]: [[value]]' : '[[value]]'; ?>",
1346
+ "bullet": "round",
1347
+ "bulletBorderAlpha": 0.6,
1348
+ "bulletColor": "#00FF00",
1349
+ "bulletSize": 4,
1350
+ "hideBulletsCount": 50,
1351
+ "lineColor": "<?php echo $chart_colors[ 1 ] ?>",
1352
+ "lineThickness": 2,
1353
+ "title": "<?php echo htmlspecialchars( $_args[ 'chart_labels' ][ 1 ], ENT_QUOTES, 'UTF-8' ); ?>",
1354
+ "type": "line",
1355
+ "useLineColorForBulletBorder": true,
1356
+ "valueField": "v2"
1357
+ }
1358
+ ],
1359
+ "dataProvider": <?php unset( $data[ 'keys' ] ); echo json_encode( $data ) ?>
1360
+ });
1361
+ <?php if ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ): ?>
1362
+ });
1363
+ <?php endif; ?>
1364
+ </script>
1365
+ <?php
1366
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1367
+ die();
1368
+ }
1369
+ }
1370
+
1371
+ public static function get_about_wpslimstat() {
1372
+ $dt = wp_slimstat_db::get_oldest_visit( '1=1', false );
1373
+ $results = array();
1374
+
1375
+ $results[ 0 ][ 'metric' ] = __( 'Dataset Size', 'wp-slimstat' );
1376
+ $results[ 0 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', '1=1', false ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1377
+ $results[ 0 ][ 'tooltip' ] = __( 'Total number of records stored in the database.', 'wp-slimstat' );
1378
+
1379
+ $results[ 1 ][ 'metric' ] = __( 'DB Size', 'wp-slimstat' );
1380
+ $results[ 1 ][ 'value' ] = wp_slimstat_db::get_data_size();
1381
+
1382
+ $results[ 2 ][ 'metric' ] = __( 'Tracking Enabled', 'wp-slimstat' );
1383
+ $results[ 2 ][ 'value' ] = __( ucfirst( wp_slimstat::$settings[ 'is_tracking' ] ), 'wp-slimstat' );
1384
+
1385
+ $results[ 3 ][ 'metric' ] = __( 'Javascript Mode', 'wp-slimstat' );
1386
+ $results[ 3 ][ 'value' ] = __( ucfirst( wp_slimstat::$settings[ 'javascript_mode' ] ), 'wp-slimstat' );
1387
+
1388
+ $results[ 4 ][ 'metric' ] = __( 'Tracking Browser Caps', 'wp-slimstat' );
1389
+ $results[ 4 ][ 'value' ] = __( ucfirst( wp_slimstat::$settings[ 'enable_javascript' ] ), 'wp-slimstat' );
1390
+
1391
+ $results[ 5 ][ 'metric' ] = __( 'Auto purge', 'wp-slimstat' );
1392
+ $results[ 5 ][ 'value' ] = ( wp_slimstat::$settings[ 'auto_purge' ] > 0 ) ? wp_slimstat::$settings[ 'auto_purge' ] . ' ' . __( 'days', 'wp-slimstat' ) : __( 'Off', 'wp-slimstat' );
1393
+
1394
+ $results[ 6 ][ 'metric' ] = __( 'Oldest pageview', 'wp-slimstat' );
1395
+ $results[ 6 ][ 'value' ] = ( $dt == null ) ? __( 'No visits', 'wp-slimstat' ) : date_i18n( wp_slimstat::$settings[ 'date_format' ], $dt );
1396
+
1397
+ $results[ 7 ][ 'metric' ] = __( 'Geolocation', 'wp-slimstat' );
1398
+ $results[ 7 ][ 'value' ] = date_i18n( wp_slimstat::$settings[ 'date_format' ], @filemtime( wp_slimstat::$maxmind_path ) );
1399
+ $results[ 7 ][ 'tooltip' ] = __( 'Date when the MaxMind Geolocation database was last updated.', 'wp-slimstat' );
1400
+
1401
+ return $results;
1402
+ }
1403
+
1404
+ public static function get_overview_summary() {
1405
+ $days_in_range = ceil( ( wp_slimstat_db::$filters_normalized[ 'utime' ][ 'end' ] - wp_slimstat_db::$filters_normalized[ 'utime' ][ 'start' ] ) / 86400 );
1406
+ $results = array();
1407
+
1408
+ $results[ 0 ][ 'metric' ] = __( 'Pageviews', 'wp-slimstat' );
1409
+ $results[ 0 ][ 'value' ] = number_format( self::$pageviews, 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1410
+ $results[ 0 ][ 'tooltip' ] = __( 'A request to load a single HTML file. Slimstat logs a "pageview" each time the tracking code is executed.', 'wp-slimstat' );
1411
+
1412
+ $results[ 1 ][ 'metric' ] = __( 'Days in Range', 'wp-slimstat' );
1413
+ $results[ 1 ][ 'value' ] = $days_in_range;
1414
+
1415
+ $results[ 2 ][ 'metric' ] = __( 'Average Daily Pageviews', 'wp-slimstat' );
1416
+ $results[ 2 ][ 'value' ] = number_format( round( self::$pageviews / $days_in_range, 0 ), 0, '', wp_slimstat_db::$formats['thousand'] );
1417
+ $results[ 2 ][ 'tooltip' ] = __( 'How many pages have been visited on average every day during the current period.', 'wp-slimstat' );
1418
+
1419
+ $results[ 3 ][ 'metric' ] = __( 'From Search Results', 'wp-slimstat' );
1420
+ $results[ 3 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'searchterms IS NOT NULL' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1421
+ $results[ 3 ][ 'tooltip' ] = __( 'Visitors who landed on your site after searching for a keyword on Google, Yahoo, etc.', 'wp-slimstat' );
1422
+
1423
+ $results[ 4 ][ 'metric' ] = __( 'Unique IPs', 'wp-slimstat' );
1424
+ $results[ 4 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'ip' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1425
+ $results[ 4 ][ 'tooltip' ] = __( 'Used to differentiate between multiple requests to download a file from one internet address (IP) and requests originating from many distinct addresses', 'wp-slimstat' );
1426
+
1427
+ $results[ 5 ][ 'metric' ] = __( 'Last 30 minutes', 'wp-slimstat' );
1428
+ $results[ 5 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'dt > ' . ( date_i18n( 'U' ) - 1800 ), false ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1429
+
1430
+ $results[ 6 ][ 'metric' ] = __( 'Today', 'wp-slimstat' );
1431
+ $results[ 6 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'dt > ' . ( date_i18n( 'U', mktime( 0, 0, 0, date_i18n( 'm' ), date_i18n( 'd' ), date_i18n( 'Y' ) ) ) ), false ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1432
+
1433
+ $results[ 7 ][ 'metric' ] = __( 'Yesterday', 'wp-slimstat' );
1434
+ $results[ 7 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'dt BETWEEN ' . ( date_i18n( 'U', mktime( 0, 0, 0, date_i18n( 'm' ), date_i18n( 'd' ) - 1, date_i18n( 'Y' ) ) ) ) . ' AND ' . ( date_i18n( 'U', mktime( 23, 59, 59, date_i18n( 'm' ), date_i18n( 'd' ) - 1, date_i18n( 'Y' ) ) ) ), false ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1435
+
1436
+ return $results;
1437
+ }
1438
+
1439
+ public static function get_plugins() {
1440
+ $wp_slim_plugins = array( 'flash', 'silverlight', 'acrobat', 'java', 'mediaplayer', 'director', 'real', 'quicktime' );
1441
+ $total_human_hits = wp_slimstat_db::count_records( 'id', 'visit_id > 0 AND browser_type <> 1' );
1442
+ $results = array();
1443
+
1444
+ foreach ( $wp_slim_plugins as $i => $a_plugin ) {
1445
+ $count_results = wp_slimstat_db::count_records( 'id', "plugins LIKE '%{$a_plugin}%'" );
1446
+ $results[ $i ][ 'metric' ] = ucfirst( $a_plugin );
1447
+ $results[ $i ][ 'value' ] = ( $total_human_hits > 0 ) ? number_format( ( 100 * $count_results / $total_human_hits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0;
1448
+ $results[ $i ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1449
+ }
1450
+
1451
+ return $results;
1452
+ }
1453
+
1454
+ public static function get_visitors_summary() {
1455
+ $results = array();
1456
+ $total_human_hits = wp_slimstat_db::count_records( 'id', 'visit_id > 0 AND browser_type <> 1');
1457
+ $new_visitors = wp_slimstat_db::count_records_having( 'ip', 'visit_id > 0 AND browser_type <> 1', 'COUNT(visit_id) = 1' );
1458
+ $new_visitors_rate = ( $total_human_hits > 0) ? ( 100 * $new_visitors / $total_human_hits ) : 0;
1459
+ $metrics_per_visit = wp_slimstat_db::get_max_and_average_pages_per_visit();
1460
+ if ( empty( $metrics_per_visit[ 0 ] ) ) {
1461
+ $metrics_per_visit[ 0 ] = array( 'avghits' => 0, 'maxhits' => 0);
1462
+ }
1463
+ if ( intval( $new_visitors_rate ) > 99 ) {
1464
+ $new_visitors_rate = '100';
1465
+ }
1466
+
1467
+ $results[ 0 ][ 'metric' ] = __( 'Visits', 'wp-slimstat' );
1468
+ $results[ 0 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'visit_id', 'visit_id > 0 AND browser_type <> 1' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1469
+ $results[ 0 ][ 'tooltip' ] = __( 'A visit is a session of at most 30 minutes. Returning visitors are counted multiple times if they perform multiple visits.', 'wp-slimstat' );
1470
+
1471
+ $results[ 1 ][ 'metric' ] = __( 'Unique IPs', 'wp-slimstat' );
1472
+ $results[ 1 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'ip', 'visit_id > 0 AND browser_type <> 1' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1473
+ $results[ 1 ][ 'tooltip' ] = __( 'It includes only traffic generated by human visitors.', 'wp-slimstat' );
1474
+
1475
+ $results[ 2 ][ 'metric' ] = __( 'Bounce rate', 'wp-slimstat' );
1476
+ $results[ 2 ][ 'value' ] = number_format( $new_visitors_rate, 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] );
1477
+ $results[ 2 ][ 'tooltip' ] = __( 'Percentage of single-page visits, i.e. visits in which the person left your site from the entrance page.', 'wp-slimstat' );
1478
+
1479
+ $results[ 3 ][ 'metric' ] = __( 'Known visitors', 'wp-slimstat' );
1480
+ $results[ 3 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'username' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1481
+ $results[ 3 ][ 'tooltip' ] = __( 'Visitors who had previously left a comment on your blog.', 'wp-slimstat' );
1482
+
1483
+ $results[ 4 ][ 'metric' ] = __( 'New visitors', 'wp-slimstat' );
1484
+ $results[ 4 ][ 'value' ] = number_format( $new_visitors, 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1485
+ $results[ 4 ][ 'tooltip' ] = __( 'Human users who visited your site only once.', 'wp-slimstat' );
1486
+
1487
+ $results[ 5 ][ 'metric' ] = __( 'Bots', 'wp-slimstat' );
1488
+ $results[ 5 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'browser_type = 1' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1489
+
1490
+ $results[ 6 ][ 'metric' ] = __( 'Pageviews per visit', 'wp-slimstat' );
1491
+ $results[ 6 ][ 'value' ] = number_format( $metrics_per_visit[ 0 ][ 'avghits' ], 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] );
1492
+
1493
+ $results[ 7 ][ 'metric' ] = __( 'Longest visit', 'wp-slimstat' );
1494
+ $results[ 7 ][ 'value' ] = number_format( $metrics_per_visit[ 0 ][ 'maxhits' ], 0, '', wp_slimstat_db::$formats[ 'thousand' ] ) . ' ' . __( 'hits', 'wp-slimstat' );
1495
+
1496
+ return $results;
1497
+ }
1498
+
1499
+ public static function get_visits_duration() {
1500
+ $total_human_visits = wp_slimstat_db::count_records( 'visit_id', 'visit_id > 0 AND browser_type <> 1' );
1501
+ $results = array();
1502
+
1503
+ $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', ' GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) >= 0 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 30' );
1504
+ $average_time = 30 * $count_results;
1505
+ $results[ 0 ][ 'metric' ] = __( '0 - 30 seconds', 'wp-slimstat' );
1506
+ $results[ 0 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1507
+ $results[ 0 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1508
+
1509
+ $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 30 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 60' );
1510
+ $average_time += 60 * $count_results;
1511
+ $results[ 1 ][ 'metric' ] = __( '31 - 60 seconds', 'wp-slimstat' );
1512
+ $results[ 1 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1513
+ $results[ 1 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1514
+
1515
+ $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 60 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 180' );
1516
+ $average_time += 180 * $count_results;
1517
+ $results[ 2 ][ 'metric' ] = __( '1 - 3 minutes', 'wp-slimstat' );
1518
+ $results[ 2 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1519
+ $results[ 2 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1520
+
1521
+ $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 180 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 300' );
1522
+ $average_time += 300 * $count_results;
1523
+ $results[ 3 ][ 'metric' ] = __( '3 - 5 minutes', 'wp-slimstat' );
1524
+ $results[ 3 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1525
+ $results[ 3 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1526
+
1527
+ $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 300 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 420' );
1528
+ $average_time += 420 * $count_results;
1529
+ $results[ 4 ][ 'metric' ] = __( '5 - 7 minutes', 'wp-slimstat' );
1530
+ $results[ 4 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1531
+ $results[ 4 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1532
+
1533
+ $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 420 AND GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) <= 600' );
1534
+ $average_time += 600* $count_results;
1535
+ $results[ 5 ][ 'metric' ] = __( '7 - 10 minutes', 'wp-slimstat' );
1536
+ $results[ 5 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1537
+ $results[ 5 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1538
+
1539
+ $count_results = wp_slimstat_db::count_records_having( 'visit_id', 'visit_id > 0 AND browser_type <> 1', 'GREATEST( MAX( dt ), MAX( dt_out ) ) - MIN( dt ) > 600' );
1540
+ $average_time += 900 * $count_results;
1541
+ $results[ 6 ][ 'metric' ] = __( 'More than 10 minutes', 'wp-slimstat' );
1542
+ $results[ 6 ][ 'value' ] = ( ( $total_human_visits > 0 ) ? number_format( ( 100 * $count_results / $total_human_visits ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) : 0 ) . '%';
1543
+ $results[ 6 ][ 'details' ] = __( 'Hits', 'wp-slimstat' ) . ": $count_results";
1544
+
1545
+ if ( $total_human_visits > 0 ) {
1546
+ $average_time /= $total_human_visits;
1547
+ $average_time = date('m:s', intval($average_time));
1548
+ }
1549
+ else{
1550
+ $average_time = '0:00';
1551
+ }
1552
+
1553
+ $results[ 7 ][ 'metric' ] = __( 'Average visit duration', 'wp-slimstat' );
1554
+ $results[ 7 ][ 'value' ] = $average_time;
1555
+ $results[ 7 ][ 'details' ] = '';
1556
+
1557
+ return $results;
1558
+ }
1559
+
1560
+ public static function get_traffic_sources_summary() {
1561
+ $results = array();
1562
+ $total_human_hits = wp_slimstat_db::count_records( 'id', 'visit_id > 0 AND browser_type <> 1' );
1563
+ $new_visitors = wp_slimstat_db::count_records_having( 'ip', 'visit_id > 0', 'COUNT(visit_id) = 1' );
1564
+ $new_visitors_rate = ($total_human_hits > 0)?sprintf("%01.2f", (100*$new_visitors/$total_human_hits)):0;
1565
+ if (intval($new_visitors_rate) > 99) {
1566
+ $new_visitors_rate = '100';
1567
+ }
1568
+
1569
+ $results[ 0 ][ 'metric' ] = __( 'Pageviews', 'wp-slimstat' );
1570
+ $results[ 0 ][ 'value' ] = number_format( self::$pageviews, 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1571
+ $results[ 0 ][ 'tooltip' ] = __( 'A request to load a single HTML file. Slimstat logs a "pageview" each time the tracking code is executed.', 'wp-slimstat' );
1572
+
1573
+ $results[ 1 ][ 'metric' ] = __( 'Unique Referrers', 'wp-slimstat' );
1574
+ $results[ 1 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'referer', "referer NOT LIKE '%{$_SERVER['SERVER_NAME']}%'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1575
+ $results[ 1 ][ 'tooltip' ] = __( 'A referrer (or referring site) is the site that a visitor previously visited before following a link to your site.', 'wp-slimstat' );
1576
+
1577
+ $results[ 2 ][ 'metric' ] = __( 'Direct Pageviews', 'wp-slimstat' );
1578
+ $results[ 2 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', 'resource IS NULL' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1579
+ $results[ 2 ][ 'tooltip' ] = __( "Visitors who visited the site by typing the URL directly into their browser. <em>Direct</em> can also refer to the visitors who clicked on the links from their bookmarks/favorites, untagged links within emails, or links from documents that don't include tracking variables.", 'wp-slimstat' );
1580
+
1581
+ $results[ 3 ][ 'metric' ] = __( 'From a search result', 'wp-slimstat' );
1582
+ $results[ 3 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', "searchterms IS NOT NULL AND referer IS NOT NULL AND referer NOT LIKE '%" . home_url() . "%'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1583
+ $results[ 3 ][ 'tooltip' ] = __( "Visitors who came to your site via searches on Google or some other search engine.", 'wp-slimstat' );
1584
+
1585
+ $results[ 4 ][ 'metric' ] = __( 'Unique Landing Pages', 'wp-slimstat' );
1586
+ $results[ 4 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'resource' ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1587
+ $results[ 4 ][ 'tooltip' ] = __( "The first page that a user views during a session. This is also known as the <em>entrance page</em>. For example, if they search for 'Brooklyn Office Space,' and they land on your home page, it gets counted (for that visit) as a landing page.", 'wp-slimstat' );
1588
+
1589
+ $results[ 5 ][ 'metric' ] = __( 'Bounce Pages', 'wp-slimstat' );
1590
+ $results[ 5 ][ 'value' ] = number_format( wp_slimstat_db::count_bouncing_pages(), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1591
+ $results[ 5 ][ 'tooltip' ] = __( 'Number of single page visits to your site over the selected period.', 'wp-slimstat' );
1592
+
1593
+ $results[ 6 ][ 'metric' ] = __( 'New Visitors Rate', 'wp-slimstat' );
1594
+ $results[ 6 ][ 'value' ] = number_format( $new_visitors_rate, 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] );
1595
+ $results[ 6 ][ 'tooltip' ] = __( 'Percentage of single page visits, i.e. visits in which the person left your site from the entrance page.', 'wp-slimstat' );
1596
+
1597
+ $results[ 7 ][ 'metric' ] = __( 'Currently from search engines', 'wp-slimstat' );
1598
+ $results[ 7 ][ 'value' ] = number_format( wp_slimstat_db::count_records( 'id', "searchterms IS NOT NULL AND referer IS NOT NULL AND referer NOT LIKE '%" . home_url() . "%' AND dt > UNIX_TIMESTAMP() - 300", false ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1599
+ $results[ 7 ][ 'tooltip' ] = __( 'Visitors who visited the site in the last 5 minutes coming from a search engine.', 'wp-slimstat' );
1600
+
1601
+ return $results;
1602
+ }
1603
+
1604
+ public static function show_rankings(){
1605
+ $options = array( 'timeout' => 1, 'headers' => array( 'Accept' => 'application/json' ) );
1606
+ $site_url = parse_url( home_url(), PHP_URL_HOST );
1607
+ if ( !empty( wp_slimstat_db::$filters_normalized[ 'resource' ] ) && wp_slimstat_db::$filters_normalized[ 'resource' ][ 0 ] == 'equals' ) {
1608
+ $site_url .= wp_slimstat_db::$filters_normalized[ 'resource' ][ 1 ];
1609
+ }
1610
+ $site_url = urlencode( $site_url );
1611
+
1612
+ // Check if we have a valied transient
1613
+ if ( false === ( $rankings = get_transient( 'slimstat_ranking_values' ) ) ) {
1614
+ $rankings = array(
1615
+ 'seomoz_equity_backlinks' => array(
1616
+ 0,
1617
+ __( 'Backlinks', 'wp-slimstat' ),
1618
+ __( 'Number of external equity links to your website.', 'wp-slimstat' )
1619
+ ),
1620
+ 'seomoz_mozrank' => array(
1621
+ 0,
1622
+ __( 'MozRank', 'wp-slimstat' ),
1623
+ __( 'MozRank of the URL, in a normalized 10-point score. MozRank represents a link popularity score. It reflects the importance of any given web page on the Internet.', 'wp-slimstat' )
1624
+ ),
1625
+ 'seomoz_equity_links' => array(
1626
+ 0,
1627
+ __( 'Equity Links', 'wp-slimstat' ),
1628
+ __( 'Number of authority-passing links (including followed links and redirects, internal or external) to your website. Set the permalink filter here above to get the corresponding metrics in this report.', 'wp-slimstat' )
1629
+ ),
1630
+ 'facebook_shares' => array(
1631
+ 0,
1632
+ __( 'Facebook Shares', 'wp-slimstat' ),
1633
+ ''
1634
+ ),
1635
+ 'facebook_clicks' => array(
1636
+ 0,
1637
+ __( 'Facebook Clicks', 'wp-slimstat' ),
1638
+ __( 'How many times links to your website have been clicked on Facebook.', 'wp-slimstat' )
1639
+ ),
1640
+ 'alexa_world_rank' => array(
1641
+ 0,
1642
+ __( 'Alexa World Rank', 'wp-slimstat' ),
1643
+ __( 'Alexa is a subsidiary company of Amazon.com which provides commercial web traffic data.', 'wp-slimstat' )
1644
+ ),
1645
+ 'alexa_country_rank' => array(
1646
+ 0,
1647
+ __( 'Alexa Country Rank', 'wp-slimstat' ),
1648
+ ''
1649
+ ),
1650
+ 'alexa_popularity' => array(
1651
+ 0,
1652
+ __( 'Alexa Popularity', 'wp-slimstat' ),
1653
+ ''
1654
+ )
1655
+ );
1656
+
1657
+ if ( !empty( wp_slimstat::$settings[ 'mozcom_access_id' ] ) && !empty( wp_slimstat::$settings[ 'mozcom_secret_key' ] ) ) {
1658
+ $expiration_token = time() + 300;
1659
+ $binary_signature = @hash_hmac( 'sha1', wp_slimstat::$settings[ 'mozcom_access_id' ] . "\n" . $expiration_token, wp_slimstat::$settings[ 'mozcom_secret_key' ], true );
1660
+ $binary_signature = urlencode( base64_encode( $binary_signature ) );
1661
+
1662
+ // SeoMoz Equity Links (Backlinks) and MozRank
1663
+ $response = @wp_remote_get( 'http://lsapi.seomoz.com/linkscape/url-metrics/' . $site_url . '?Cols=16672&AccessID=' . wp_slimstat::$settings[ 'mozcom_access_id' ] . '&Expires=' . $expiration_token . '&Signature=' . $binary_signature, $options );
1664
+ if ( !is_wp_error( $response ) && isset( $response[ 'response' ][ 'code' ] ) && ( $response[ 'response' ][ 'code' ] == 200 ) && !empty( $response[ 'body' ] ) ) {
1665
+ $response = @json_decode( $response[ 'body' ] );
1666
+ if ( is_object( $response ) ) {
1667
+ if ( !empty( $response->ujid ) ) {
1668
+ $rankings[ 'seomoz_equity_links' ][ 0 ] = number_format( intval( $response->ujid ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1669
+ }
1670
+
1671
+ if ( !empty( $response->ueid ) ) {
1672
+ $rankings[ 'seomoz_equity_backlinks' ][ 0 ] = number_format( intval( $response->ueid ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1673
+ }
1674
+
1675
+ if ( !empty( $response->umrp ) ) {
1676
+ $rankings[ 'seomoz_mozrank' ][ 0 ] = number_format( floatval( $response->umrp ), 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] );
1677
+ }
1678
+ }
1679
+ }
1680
+ }
1681
+
1682
+ // Alexa
1683
+ $response = @wp_remote_get( 'http://data.alexa.com/data?cli=10&dat=snbamz&url=' . $site_url, $options );
1684
+ if ( !is_wp_error( $response ) && isset( $response[ 'response' ][ 'code' ] ) && ( $response[ 'response' ][ 'code' ] == 200 ) && !empty( $response[ 'body' ] ) ) {
1685
+ $response = @simplexml_load_string( $response[ 'body' ] );
1686
+ if ( is_object( $response->SD[ 1 ] ) ) {
1687
+ if ( $response->SD[ 1 ]->POPULARITY && $response->SD[ 1 ]->POPULARITY->attributes() ) {
1688
+ $popularity = $response->SD[ 1 ]->POPULARITY->attributes();
1689
+ if ( !empty( $popularity ) ) {
1690
+ $rankings[ 'alexa_popularity' ][ 0 ] = number_format( floatval( $popularity[ 'TEXT' ] ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1691
+ }
1692
+ }
1693
+
1694
+ if ( $response->SD[ 1 ]->REACH && $response->SD[ 1 ]->REACH->attributes() ) {
1695
+ $reach = $response->SD[ 1 ]->REACH->attributes();
1696
+ if ( !empty( $reach ) ) {
1697
+ $rankings[ 'alexa_world_rank' ][ 0 ] = number_format( floatval( $reach[ 'RANK' ] ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1698
+ }
1699
+ }
1700
+
1701
+ if ( $response->SD[ 1 ]->COUNTRY && $response->SD[ 1 ]->COUNTRY->attributes() ) {
1702
+ $country = $response->SD[ 1 ]->COUNTRY->attributes();
1703
+ if ( !empty( $country ) ) {
1704
+ $rankings[ 'alexa_country_rank' ][ 0 ] = number_format( floatval( $country[ 'RANK' ] ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1705
+ }
1706
+ }
1707
+ else if ( $response->SD[ 1 ]->RANK && $response->SD[ 1 ]->RANK->attributes() ) {
1708
+ $rank = $response->SD[ 1 ]->RANK->attributes();
1709
+ if ( !empty( $rank ) ) {
1710
+ $rankings[ 'alexa_country_rank' ][ 0 ] = number_format( floatval( $rank[ 'DELTA' ] ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1711
+ $rankings[ 'alexa_country_rank' ][ 1 ] = __( 'Alexa Delta', 'wp-slimstat' );
1712
+ }
1713
+ }
1714
+ }
1715
+ }
1716
+
1717
+ // Facebook
1718
+ $options[ 'headers' ][ 'Accept' ] = 'text/xml';
1719
+ $response = @wp_remote_get( 'http://api.facebook.com/restserver.php?method=links.getStats&urls=' . $site_url, $options );
1720
+ if ( !is_wp_error( $response ) && isset( $response[ 'response' ][ 'code' ] ) && ( $response[ 'response' ][ 'code' ] == 200 ) && !empty( $response[ 'body' ] ) ) {
1721
+ $response = @simplexml_load_string( $response[ 'body' ] );
1722
+ if ( is_object( $response ) && is_object( $response->link_stat ) ) {
1723
+ $rankings[ 'facebook_shares' ][ 0 ] = number_format( intval( $response->link_stat->share_count ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1724
+ $rankings[ 'facebook_clicks' ][ 0 ] = number_format( intval( $response->link_stat->click_count ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1725
+ }
1726
+ }
1727
+
1728
+ // Store rankings as transients for 12 hours
1729
+ //set_transient('slimstat_ranking_values', $rankings, 43200);
1730
+ }
1731
+
1732
+ foreach ( $rankings as $a_ranking ) {
1733
+ echo '<p>' . self::inline_help( $a_ranking[ 2 ], false ) . $a_ranking[ 1 ] . '<span>' . $a_ranking[ 0 ] . '</span></p>';
1734
+ }
1735
+
1736
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1737
+ die();
1738
+ }
1739
+ }
1740
+
1741
+ public static function get_your_blog(){
1742
+ if ( false === ( $results = get_transient( 'slimstat_your_content' ) ) ) {
1743
+ $results = array();
1744
+
1745
+ $results[ 0 ][ 'metric' ] = __( 'Content Items', 'wp-slimstat' );
1746
+ $results[ 0 ][ 'value' ] = number_format( $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->posts} WHERE post_type <> 'revision' AND post_status <> 'auto-draft'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1747
+ $results[ 0 ][ 'tooltip' ] = __( 'This value includes not only posts, but also custom post types, regardless of their status', 'wp-slimstat' );
1748
+
1749
+ $results[ 1 ][ 'metric' ] = __( 'Posts', 'wp-slimstat' );
1750
+ $results[ 1 ][ 'value' ] = $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->posts} WHERE post_type = 'post'" );
1751
+
1752
+ $results[ 2 ][ 'metric' ] = __( 'Pages', 'wp-slimstat' );
1753
+ $results[ 2 ][ 'value' ] = number_format( $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->posts} WHERE post_type = 'page'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1754
+
1755
+ $results[ 3 ][ 'metric' ] = __( 'Attachments', 'wp-slimstat' );
1756
+ $results[ 3 ][ 'value' ] = number_format( $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->posts} WHERE post_type = 'attachment'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1757
+
1758
+ $results[ 4 ][ 'metric' ] = __( 'Revisions', 'wp-slimstat' );
1759
+ $results[ 4 ][ 'value' ] = number_format( $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->posts} WHERE post_type = 'revision'" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1760
+
1761
+ $results[ 5 ][ 'metric' ] = __( 'Comments', 'wp-slimstat' );
1762
+ $results[ 5 ][ 'value' ] = $GLOBALS[ 'wpdb' ]->get_var( "SELECT COUNT(*) FROM {$GLOBALS['wpdb']->comments}" );
1763
+
1764
+ $results[ 6 ][ 'metric' ] = __( 'Avg Comments per Post', 'wp-slimstat' );
1765
+ $results[ 6 ][ 'value' ] = number_format( !empty( $results[ 1 ][ 'value' ] ) ? $results[ 5 ][ 'value' ] / $results[ 1 ][ 'value' ] : 0, 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1766
+
1767
+ $results[ 7 ][ 'metric' ] = __( 'Avg Server Latency', 'wp-slimstat' );
1768
+ $results[ 7 ][ 'value' ] = number_format( wp_slimstat::$wpdb->get_var( "SELECT AVG(server_latency) FROM {$GLOBALS[ 'wpdb' ]->prefix }slim_stats WHERE server_latency <> 0" ), 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1769
+ $results[ 7 ][ 'tooltip' ] = __( 'Latency is the amount of time it takes for the host server to receive and process a request for a page object. The amount of latency depends largely on how far away the user is from the server.', 'wp-slimstat' );
1770
+
1771
+ $results[ 1 ][ 'value' ] = number_format( $results[ 1 ][ 'value' ], 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1772
+ $results[ 5 ][ 'value' ] = number_format( $results[ 5 ][ 'value' ], 0, '', wp_slimstat_db::$formats[ 'thousand' ] );
1773
+
1774
+ // Store values as transients for 30 minutes
1775
+ set_transient( 'slimstat_your_content', $results, 1800 );
1776
+ }
1777
+
1778
+ return $results;
1779
+ }
1780
+
1781
+ public static function show_events( $_args = array() ) {
1782
+ $all_results = call_user_func( $_args[ 'raw' ] , $_args );
1783
+
1784
+ $results = array_slice(
1785
+ $all_results,
1786
+ wp_slimstat_db::$filters_normalized[ 'misc' ][ 'start_from' ],
1787
+ wp_slimstat::$settings[ 'rows_to_show' ]
1788
+ );
1789
+
1790
+ // Count the results
1791
+ $count_page_results = count( $results );
1792
+
1793
+ if ($count_page_results == 0){
1794
+ echo '<p class="nodata">' . __( 'No data to display', 'wp-slimstat' ) . '</p>';
1795
+
1796
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1797
+ die();
1798
+ }
1799
+ else{
1800
+ return array();
1801
+ }
1802
+ }
1803
+
1804
+ echo self::report_pagination( $count_page_results, count( $all_results ) );
1805
+ $is_expanded = ( wp_slimstat::$settings[ 'expand_details' ] == 'on' ) ? ' expanded' : '';
1806
+
1807
+ foreach ( $results as $a_result ) {
1808
+ echo "<p class='slimstat-tooltip-trigger'>{$a_result[ 'notes' ]} <b class='slimstat-tooltip-content$is_expanded'>" . __( 'Type', 'wp-slimstat' ) . ": {$a_result[ 'type' ]}";
1809
+
1810
+ if ( !empty( $a_result[ 'dt' ] ) ) {
1811
+ $date_time = date_i18n( wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ], $a_result[ 'dt' ], true );
1812
+ echo '<br/>' . __( 'Coordinates', 'wp-slimstat' ) . ": {$a_result[ 'position' ]}<br/>" . __( 'Date', 'wp-slimstat' ) . ": $date_time";
1813
+ }
1814
+ if ( !empty( $a_result[ 'counthits' ] ) ) {
1815
+ echo '<br/>' . __( 'Hits', 'wp-slimstat' ) . ": {$a_result[ 'counthits' ]}";
1816
+ }
1817
+
1818
+ echo "</b></p>";
1819
+ }
1820
+
1821
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1822
+ die();
1823
+ }
1824
+ }
1825
+
1826
+ public static function show_world_map() {
1827
+ $countries = wp_slimstat_db::get_top( 'country' );
1828
+ $recent_visits = wp_slimstat_db::get_recent( 'location', '', '', true, '', 'city' );
1829
+
1830
+ $data_points = array();
1831
+ if ( !empty( $recent_visits ) ) {
1832
+ $recent_visits = array_slice( $recent_visits, 0, wp_slimstat::$settings[ 'max_dots_on_map' ] );
1833
+
1834
+ foreach ( $recent_visits as $a_recent_visit ) {
1835
+ if ( !empty( $a_recent_visit[ 'city' ] ) && !empty( $a_recent_visit[ 'location' ] ) ) {
1836
+ list( $latitude, $longitude ) = explode( ',', $a_recent_visit[ 'location' ] );
1837
+ $clean_city_name = htmlentities( $a_recent_visit[ 'city' ], ENT_QUOTES, 'UTF-8' );
1838
+ $date_time = date_i18n( wp_slimstat::$settings[ 'date_format' ] . ' ' . wp_slimstat::$settings[ 'time_format' ], $a_recent_visit[ 'dt' ], true );
1839
+ $data_points[] = "{zoomLevel:7,type:'circle',title:'{$clean_city_name}<br>{$date_time}',latitude:$latitude,longitude:$longitude}";
1840
+ }
1841
+ }
1842
+ }
1843
+
1844
+ $data_areas = array('xx'=>'{id:"XX",balloonText:"'.__('c-xx','wp-slimstat').': 0",value:0,color:"#ededed"}','af'=>'{id:"AF",balloonText:"'.__('c-af','wp-slimstat').': 0",value:0,color:"#ededed"}','ax'=>'{id:"AX",balloonText:"'.__('c-ax','wp-slimstat').': 0",value:0,color:"#ededed"}','al'=>'{id:"AL",balloonText:"'.__('c-al','wp-slimstat').': 0",value:0,color:"#ededed"}','dz'=>'{id:"DZ",balloonText:"'.__('c-dz','wp-slimstat').': 0",value:0,color:"#ededed"}','ad'=>'{id:"AD",balloonText:"'.__('c-ad','wp-slimstat').': 0",value:0,color:"#ededed"}','ao'=>'{id:"AO",balloonText:"'.__('c-ao','wp-slimstat').': 0",value:0,color:"#ededed"}','ai'=>'{id:"AI",balloonText:"'.__('c-ai','wp-slimstat').': 0",value:0,color:"#ededed"}','ag'=>'{id:"AG",balloonText:"'.__('c-ag','wp-slimstat').': 0",value:0,color:"#ededed"}','ar'=>'{id:"AR",balloonText:"'.__('c-ar','wp-slimstat').': 0",value:0,color:"#ededed"}','am'=>'{id:"AM",balloonText:"'.__('c-am','wp-slimstat').': 0",value:0,color:"#ededed"}','aw'=>'{id:"AW",balloonText:"'.__('c-aw','wp-slimstat').': 0",value:0,color:"#ededed"}','au'=>'{id:"AU",balloonText:"'.__('c-au','wp-slimstat').': 0",value:0,color:"#ededed"}','at'=>'{id:"AT",balloonText:"'.__('c-at','wp-slimstat').': 0",value:0,color:"#ededed"}','az'=>'{id:"AZ",balloonText:"'.__('c-az','wp-slimstat').': 0",value:0,color:"#ededed"}','bs'=>'{id:"BS",balloonText:"'.__('c-bs','wp-slimstat').': 0",value:0,color:"#ededed"}','bh'=>'{id:"BH",balloonText:"'.__('c-bh','wp-slimstat').': 0",value:0,color:"#ededed"}','bd'=>'{id:"BD",balloonText:"'.__('c-bd','wp-slimstat').': 0",value:0,color:"#ededed"}','bb'=>'{id:"BB",balloonText:"'.__('c-bb','wp-slimstat').': 0",value:0,color:"#ededed"}','by'=>'{id:"BY",balloonText:"'.__('c-by','wp-slimstat').': 0",value:0,color:"#ededed"}','be'=>'{id:"BE",balloonText:"'.__('c-be','wp-slimstat').': 0",value:0,color:"#ededed"}','bz'=>'{id:"BZ",balloonText:"'.__('c-bz','wp-slimstat').': 0",value:0,color:"#ededed"}','bj'=>'{id:"BJ",balloonText:"'.__('c-bj','wp-slimstat').': 0",value:0,color:"#ededed"}','bm'=>'{id:"BM",balloonText:"'.__('c-bm','wp-slimstat').': 0",value:0,color:"#ededed"}','bt'=>'{id:"BT",balloonText:"'.__('c-bt','wp-slimstat').': 0",value:0,color:"#ededed"}','bo'=>'{id:"BO",balloonText:"'.__('c-bo','wp-slimstat').': 0",value:0,color:"#ededed"}','ba'=>'{id:"BA",balloonText:"'.__('c-ba','wp-slimstat').': 0",value:0,color:"#ededed"}','bw'=>'{id:"BW",balloonText:"'.__('c-bw','wp-slimstat').': 0",value:0,color:"#ededed"}','br'=>'{id:"BR",balloonText:"'.__('c-br','wp-slimstat').': 0",value:0,color:"#ededed"}','bn'=>'{id:"BN",balloonText:"'.__('c-bn','wp-slimstat').': 0",value:0,color:"#ededed"}','bg'=>'{id:"BG",balloonText:"'.__('c-bg','wp-slimstat').': 0",value:0,color:"#ededed"}','bf'=>'{id:"BF",balloonText:"'.__('c-bf','wp-slimstat').': 0",value:0,color:"#ededed"}','bi'=>'{id:"BI",balloonText:"'.__('c-bi','wp-slimstat').': 0",value:0,color:"#ededed"}','kh'=>'{id:"KH",balloonText:"'.__('c-kh','wp-slimstat').': 0",value:0,color:"#ededed"}','cm'=>'{id:"CM",balloonText:"'.__('c-cm','wp-slimstat').': 0",value:0,color:"#ededed"}','ca'=>'{id:"CA",balloonText:"'.__('c-ca','wp-slimstat').': 0",value:0,color:"#ededed"}','cv'=>'{id:"CV",balloonText:"'.__('c-cv','wp-slimstat').': 0",value:0,color:"#ededed"}','ky'=>'{id:"KY",balloonText:"'.__('c-ky','wp-slimstat').': 0",value:0,color:"#ededed"}','cf'=>'{id:"CF",balloonText:"'.__('c-cf','wp-slimstat').': 0",value:0,color:"#ededed"}','td'=>'{id:"TD",balloonText:"'.__('c-td','wp-slimstat').': 0",value:0,color:"#ededed"}','cl'=>'{id:"CL",balloonText:"'.__('c-cl','wp-slimstat').': 0",value:0,color:"#ededed"}','cn'=>'{id:"CN",balloonText:"'.__('c-cn','wp-slimstat').': 0",value:0,color:"#ededed"}','co'=>'{id:"CO",balloonText:"'.__('c-co','wp-slimstat').': 0",value:0,color:"#ededed"}','km'=>'{id:"KM",balloonText:"'.__('c-km','wp-slimstat').': 0",value:0,color:"#ededed"}','cg'=>'{id:"CG",balloonText:"'.__('c-cg','wp-slimstat').': 0",value:0,color:"#ededed"}','cd'=>'{id:"CD",balloonText:"'.__('c-cd','wp-slimstat').': 0",value:0,color:"#ededed"}','cr'=>'{id:"CR",balloonText:"'.__('c-cr','wp-slimstat').': 0",value:0,color:"#ededed"}','ci'=>'{id:"CI",balloonText:"'.__('c-ci','wp-slimstat').': 0",value:0,color:"#ededed"}','hr'=>'{id:"HR",balloonText:"'.__('c-hr','wp-slimstat').': 0",value:0,color:"#ededed"}','cu'=>'{id:"CU",balloonText:"'.__('c-cu','wp-slimstat').': 0",value:0,color:"#ededed"}','cy'=>'{id:"CY",balloonText:"'.__('c-cy','wp-slimstat').': 0",value:0,color:"#ededed"}','cz'=>'{id:"CZ",balloonText:"'.__('c-cz','wp-slimstat').': 0",value:0,color:"#ededed"}','dk'=>'{id:"DK",balloonText:"'.__('c-dk','wp-slimstat').': 0",value:0,color:"#ededed"}','dj'=>'{id:"DJ",balloonText:"'.__('c-dj','wp-slimstat').': 0",value:0,color:"#ededed"}','dm'=>'{id:"DM",balloonText:"'.__('c-dm','wp-slimstat').': 0",value:0,color:"#ededed"}','do'=>'{id:"DO",balloonText:"'.__('c-do','wp-slimstat').': 0",value:0,color:"#ededed"}','ec'=>'{id:"EC",balloonText:"'.__('c-ec','wp-slimstat').': 0",value:0,color:"#ededed"}','eg'=>'{id:"EG",balloonText:"'.__('c-eg','wp-slimstat').': 0",value:0,color:"#ededed"}','sv'=>'{id:"SV",balloonText:"'.__('c-sv','wp-slimstat').': 0",value:0,color:"#ededed"}','gq'=>'{id:"GQ",balloonText:"'.__('c-gq','wp-slimstat').': 0",value:0,color:"#ededed"}','er'=>'{id:"ER",balloonText:"'.__('c-er','wp-slimstat').': 0",value:0,color:"#ededed"}','ee'=>'{id:"EE",balloonText:"'.__('c-ee','wp-slimstat').': 0",value:0,color:"#ededed"}','et'=>'{id:"ET",balloonText:"'.__('c-et','wp-slimstat').': 0",value:0,color:"#ededed"}','fo'=>'{id:"FO",balloonText:"'.__('c-fo','wp-slimstat').': 0",value:0,color:"#ededed"}','fk'=>'{id:"FK",balloonText:"'.__('c-fk','wp-slimstat').': 0",value:0,color:"#ededed"}','fj'=>'{id:"FJ",balloonText:"'.__('c-fj','wp-slimstat').': 0",value:0,color:"#ededed"}','fi'=>'{id:"FI",balloonText:"'.__('c-fi','wp-slimstat').': 0",value:0,color:"#ededed"}','fr'=>'{id:"FR",balloonText:"'.__('c-fr','wp-slimstat').': 0",value:0,color:"#ededed"}','gf'=>'{id:"GF",balloonText:"'.__('c-gf','wp-slimstat').': 0",value:0,color:"#ededed"}','ga'=>'{id:"GA",balloonText:"'.__('c-ga','wp-slimstat').': 0",value:0,color:"#ededed"}','gm'=>'{id:"GM",balloonText:"'.__('c-gm','wp-slimstat').': 0",value:0,color:"#ededed"}','ge'=>'{id:"GE",balloonText:"'.__('c-ge','wp-slimstat').': 0",value:0,color:"#ededed"}','de'=>'{id:"DE",balloonText:"'.__('c-de','wp-slimstat').': 0",value:0,color:"#ededed"}','gh'=>'{id:"GH",balloonText:"'.__('c-gh','wp-slimstat').': 0",value:0,color:"#ededed"}','gr'=>'{id:"GR",balloonText:"'.__('c-gr','wp-slimstat').': 0",value:0,color:"#ededed"}','gl'=>'{id:"GL",balloonText:"'.__('c-gl','wp-slimstat').': 0",value:0,color:"#ededed"}','gd'=>'{id:"GD",balloonText:"'.__('c-gd','wp-slimstat').': 0",value:0,color:"#ededed"}','gp'=>'{id:"GP",balloonText:"'.__('c-gp','wp-slimstat').': 0",value:0,color:"#ededed"}','gt'=>'{id:"GT",balloonText:"'.__('c-gt','wp-slimstat').': 0",value:0,color:"#ededed"}','gn'=>'{id:"GN",balloonText:"'.__('c-gn','wp-slimstat').': 0",value:0,color:"#ededed"}','gw'=>'{id:"GW",balloonText:"'.__('c-gw','wp-slimstat').': 0",value:0,color:"#ededed"}','gy'=>'{id:"GY",balloonText:"'.__('c-gy','wp-slimstat').': 0",value:0,color:"#ededed"}','ht'=>'{id:"HT",balloonText:"'.__('c-ht','wp-slimstat').': 0",value:0,color:"#ededed"}','hn'=>'{id:"HN",balloonText:"'.__('c-hn','wp-slimstat').': 0",value:0,color:"#ededed"}','hk'=>'{id:"HK",balloonText:"'.__('c-hk','wp-slimstat').': 0",value:0,color:"#ededed"}','hu'=>'{id:"HU",balloonText:"'.__('c-hu','wp-slimstat').': 0",value:0,color:"#ededed"}','is'=>'{id:"IS",balloonText:"'.__('c-is','wp-slimstat').': 0",value:0,color:"#ededed"}','in'=>'{id:"IN",balloonText:"'.__('c-in','wp-slimstat').': 0",value:0,color:"#ededed"}','id'=>'{id:"ID",balloonText:"'.__('c-id','wp-slimstat').': 0",value:0,color:"#ededed"}','ir'=>'{id:"IR",balloonText:"'.__('c-ir','wp-slimstat').': 0",value:0,color:"#ededed"}','iq'=>'{id:"IQ",balloonText:"'.__('c-iq','wp-slimstat').': 0",value:0,color:"#ededed"}','ie'=>'{id:"IE",balloonText:"'.__('c-ie','wp-slimstat').': 0",value:0,color:"#ededed"}','il'=>'{id:"IL",balloonText:"'.__('c-il','wp-slimstat').': 0",value:0,color:"#ededed"}','it'=>'{id:"IT",balloonText:"'.__('c-it','wp-slimstat').': 0",value:0,color:"#ededed"}','jm'=>'{id:"JM",balloonText:"'.__('c-jm','wp-slimstat').': 0",value:0,color:"#ededed"}','jp'=>'{id:"JP",balloonText:"'.__('c-jp','wp-slimstat').': 0",value:0,color:"#ededed"}','jo'=>'{id:"JO",balloonText:"'.__('c-jo','wp-slimstat').': 0",value:0,color:"#ededed"}','kz'=>'{id:"KZ",balloonText:"'.__('c-kz','wp-slimstat').': 0",value:0,color:"#ededed"}','ke'=>'{id:"KE",balloonText:"'.__('c-ke','wp-slimstat').': 0",value:0,color:"#ededed"}','nr'=>'{id:"NR",balloonText:"'.__('c-nr','wp-slimstat').': 0",value:0,color:"#ededed"}','kp'=>'{id:"KP",balloonText:"'.__('c-kp','wp-slimstat').': 0",value:0,color:"#ededed"}','kr'=>'{id:"KR",balloonText:"'.__('c-kr','wp-slimstat').': 0",value:0,color:"#ededed"}','kv'=>'{id:"KV",balloonText:"'.__('c-kv','wp-slimstat').': 0",value:0,color:"#ededed"}','kw'=>'{id:"KW",balloonText:"'.__('c-kw','wp-slimstat').': 0",value:0,color:"#ededed"}','kg'=>'{id:"KG",balloonText:"'.__('c-kg','wp-slimstat').': 0",value:0,color:"#ededed"}','la'=>'{id:"LA",balloonText:"'.__('c-la','wp-slimstat').': 0",value:0,color:"#ededed"}','lv'=>'{id:"LV",balloonText:"'.__('c-lv','wp-slimstat').': 0",value:0,color:"#ededed"}','lb'=>'{id:"LB",balloonText:"'.__('c-lb','wp-slimstat').': 0",value:0,color:"#ededed"}','ls'=>'{id:"LS",balloonText:"'.__('c-ls','wp-slimstat').': 0",value:0,color:"#ededed"}','lr'=>'{id:"LR",balloonText:"'.__('c-lr','wp-slimstat').': 0",value:0,color:"#ededed"}','ly'=>'{id:"LY",balloonText:"'.__('c-ly','wp-slimstat').': 0",value:0,color:"#ededed"}','li'=>'{id:"LI",balloonText:"'.__('c-li','wp-slimstat').': 0",value:0,color:"#ededed"}','lt'=>'{id:"LT",balloonText:"'.__('c-lt','wp-slimstat').': 0",value:0,color:"#ededed"}','lu'=>'{id:"LU",balloonText:"'.__('c-lu','wp-slimstat').': 0",value:0,color:"#ededed"}','mk'=>'{id:"MK",balloonText:"'.__('c-mk','wp-slimstat').': 0",value:0,color:"#ededed"}','mg'=>'{id:"MG",balloonText:"'.__('c-mg','wp-slimstat').': 0",value:0,color:"#ededed"}','mw'=>'{id:"MW",balloonText:"'.__('c-mw','wp-slimstat').': 0",value:0,color:"#ededed"}','my'=>'{id:"MY",balloonText:"'.__('c-my','wp-slimstat').': 0",value:0,color:"#ededed"}','ml'=>'{id:"ML",balloonText:"'.__('c-ml','wp-slimstat').': 0",value:0,color:"#ededed"}','mt'=>'{id:"MT",balloonText:"'.__('c-mt','wp-slimstat').': 0",value:0,color:"#ededed"}','mq'=>'{id:"MQ",balloonText:"'.__('c-mq','wp-slimstat').': 0",value:0,color:"#ededed"}','mr'=>'{id:"MR",balloonText:"'.__('c-mr','wp-slimstat').': 0",value:0,color:"#ededed"}','mu'=>'{id:"MU",balloonText:"'.__('c-mu','wp-slimstat').': 0",value:0,color:"#ededed"}','mx'=>'{id:"MX",balloonText:"'.__('c-mx','wp-slimstat').': 0",value:0,color:"#ededed"}','md'=>'{id:"MD",balloonText:"'.__('c-md','wp-slimstat').': 0",value:0,color:"#ededed"}','mn'=>'{id:"MN",balloonText:"'.__('c-mn','wp-slimstat').': 0",value:0,color:"#ededed"}','me'=>'{id:"ME",balloonText:"'.__('c-me','wp-slimstat').': 0",value:0,color:"#ededed"}','ms'=>'{id:"MS",balloonText:"'.__('c-ms','wp-slimstat').': 0",value:0,color:"#ededed"}','ma'=>'{id:"MA",balloonText:"'.__('c-ma','wp-slimstat').': 0",value:0,color:"#ededed"}','mz'=>'{id:"MZ",balloonText:"'.__('c-mz','wp-slimstat').': 0",value:0,color:"#ededed"}','mm'=>'{id:"MM",balloonText:"'.__('c-mm','wp-slimstat').': 0",value:0,color:"#ededed"}','na'=>'{id:"NA",balloonText:"'.__('c-na','wp-slimstat').': 0",value:0,color:"#ededed"}','np'=>'{id:"NP",balloonText:"'.__('c-np','wp-slimstat').': 0",value:0,color:"#ededed"}','nl'=>'{id:"NL",balloonText:"'.__('c-nl','wp-slimstat').': 0",value:0,color:"#ededed"}','nc'=>'{id:"NC",balloonText:"'.__('c-nc','wp-slimstat').': 0",value:0,color:"#ededed"}','nz'=>'{id:"NZ",balloonText:"'.__('c-nz','wp-slimstat').': 0",value:0,color:"#ededed"}','ni'=>'{id:"NI",balloonText:"'.__('c-ni','wp-slimstat').': 0",value:0,color:"#ededed"}','ne'=>'{id:"NE",balloonText:"'.__('c-ne','wp-slimstat').': 0",value:0,color:"#ededed"}','ng'=>'{id:"NG",balloonText:"'.__('c-ng','wp-slimstat').': 0",value:0,color:"#ededed"}','no'=>'{id:"NO",balloonText:"'.__('c-no','wp-slimstat').': 0",value:0,color:"#ededed"}','om'=>'{id:"OM",balloonText:"'.__('c-om','wp-slimstat').': 0",value:0,color:"#ededed"}','pk'=>'{id:"PK",balloonText:"'.__('c-pk','wp-slimstat').': 0",value:0,color:"#ededed"}','pw'=>'{id:"PW",balloonText:"'.__('c-pw','wp-slimstat').': 0",value:0,color:"#ededed"}','ps'=>'{id:"PS",balloonText:"'.__('c-ps','wp-slimstat').': 0",value:0,color:"#ededed"}','pa'=>'{id:"PA",balloonText:"'.__('c-pa','wp-slimstat').': 0",value:0,color:"#ededed"}','pg'=>'{id:"PG",balloonText:"'.__('c-pg','wp-slimstat').': 0",value:0,color:"#ededed"}','py'=>'{id:"PY",balloonText:"'.__('c-py','wp-slimstat').': 0",value:0,color:"#ededed"}','pe'=>'{id:"PE",balloonText:"'.__('c-pe','wp-slimstat').': 0",value:0,color:"#ededed"}','ph'=>'{id:"PH",balloonText:"'.__('c-ph','wp-slimstat').': 0",value:0,color:"#ededed"}','pl'=>'{id:"PL",balloonText:"'.__('c-pl','wp-slimstat').': 0",value:0,color:"#ededed"}','pt'=>'{id:"PT",balloonText:"'.__('c-pt','wp-slimstat').': 0",value:0,color:"#ededed"}','pr'=>'{id:"PR",balloonText:"'.__('c-pr','wp-slimstat').': 0",value:0,color:"#ededed"}','qa'=>'{id:"QA",balloonText:"'.__('c-qa','wp-slimstat').': 0",value:0,color:"#ededed"}','re'=>'{id:"RE",balloonText:"'.__('c-re','wp-slimstat').': 0",value:0,color:"#ededed"}','ro'=>'{id:"RO",balloonText:"'.__('c-ro','wp-slimstat').': 0",value:0,color:"#ededed"}','ru'=>'{id:"RU",balloonText:"'.__('c-ru','wp-slimstat').': 0",value:0,color:"#ededed"}','rw'=>'{id:"RW",balloonText:"'.__('c-rw','wp-slimstat').': 0",value:0,color:"#ededed"}','kn'=>'{id:"KN",balloonText:"'.__('c-kn','wp-slimstat').': 0",value:0,color:"#ededed"}','lc'=>'{id:"LC",balloonText:"'.__('c-lc','wp-slimstat').': 0",value:0,color:"#ededed"}','mf'=>'{id:"MF",balloonText:"'.__('c-mf','wp-slimstat').': 0",value:0,color:"#ededed"}','vc'=>'{id:"VC",balloonText:"'.__('c-vc','wp-slimstat').': 0",value:0,color:"#ededed"}','ws'=>'{id:"WS",balloonText:"'.__('c-ws','wp-slimstat').': 0",value:0,color:"#ededed"}','st'=>'{id:"ST",balloonText:"'.__('c-st','wp-slimstat').': 0",value:0,color:"#ededed"}','sa'=>'{id:"SA",balloonText:"'.__('c-sa','wp-slimstat').': 0",value:0,color:"#ededed"}','sn'=>'{id:"SN",balloonText:"'.__('c-sn','wp-slimstat').': 0",value:0,color:"#ededed"}','rs'=>'{id:"RS",balloonText:"'.__('c-rs','wp-slimstat').': 0",value:0,color:"#ededed"}','sl'=>'{id:"SL",balloonText:"'.__('c-sl','wp-slimstat').': 0",value:0,color:"#ededed"}','sg'=>'{id:"SG",balloonText:"'.__('c-sg','wp-slimstat').': 0",value:0,color:"#ededed"}','sk'=>'{id:"SK",balloonText:"'.__('c-sk','wp-slimstat').': 0",value:0,color:"#ededed"}','si'=>'{id:"SI",balloonText:"'.__('c-si','wp-slimstat').': 0",value:0,color:"#ededed"}','sb'=>'{id:"SB",balloonText:"'.__('c-sb','wp-slimstat').': 0",value:0,color:"#ededed"}','so'=>'{id:"SO",balloonText:"'.__('c-so','wp-slimstat').': 0",value:0,color:"#ededed"}','za'=>'{id:"ZA",balloonText:"'.__('c-za','wp-slimstat').': 0",value:0,color:"#ededed"}','gs'=>'{id:"GS",balloonText:"'.__('c-gs','wp-slimstat').': 0",value:0,color:"#ededed"}','es'=>'{id:"ES",balloonText:"'.__('c-es','wp-slimstat').': 0",value:0,color:"#ededed"}','lk'=>'{id:"LK",balloonText:"'.__('c-lk','wp-slimstat').': 0",value:0,color:"#ededed"}','sc'=>'{id:"SC",balloonText:"'.__('c-sc','wp-slimstat').': 0",value:0,color:"#ededed"}','sd'=>'{id:"SD",balloonText:"'.__('c-sd','wp-slimstat').': 0",value:0,color:"#ededed"}','ss'=>'{id:"SS",balloonText:"'.__('c-ss','wp-slimstat').': 0",value:0,color:"#ededed"}','sr'=>'{id:"SR",balloonText:"'.__('c-sr','wp-slimstat').': 0",value:0,color:"#ededed"}','sj'=>'{id:"SJ",balloonText:"'.__('c-sj','wp-slimstat').': 0",value:0,color:"#ededed"}','sz'=>'{id:"SZ",balloonText:"'.__('c-sz','wp-slimstat').': 0",value:0,color:"#ededed"}','se'=>'{id:"SE",balloonText:"'.__('c-se','wp-slimstat').': 0",value:0,color:"#ededed"}','ch'=>'{id:"CH",balloonText:"'.__('c-ch','wp-slimstat').': 0",value:0,color:"#ededed"}','sy'=>'{id:"SY",balloonText:"'.__('c-sy','wp-slimstat').': 0",value:0,color:"#ededed"}','tw'=>'{id:"TW",balloonText:"'.__('c-tw','wp-slimstat').': 0",value:0,color:"#ededed"}','tj'=>'{id:"TJ",balloonText:"'.__('c-tj','wp-slimstat').': 0",value:0,color:"#ededed"}','tz'=>'{id:"TZ",balloonText:"'.__('c-tz','wp-slimstat').': 0",value:0,color:"#ededed"}','th'=>'{id:"TH",balloonText:"'.__('c-th','wp-slimstat').': 0",value:0,color:"#ededed"}','tl'=>'{id:"TL",balloonText:"'.__('c-tl','wp-slimstat').': 0",value:0,color:"#ededed"}','tg'=>'{id:"TG",balloonText:"'.__('c-tg','wp-slimstat').': 0",value:0,color:"#ededed"}','to'=>'{id:"TO",balloonText:"'.__('c-to','wp-slimstat').': 0",value:0,color:"#ededed"}','tt'=>'{id:"TT",balloonText:"'.__('c-tt','wp-slimstat').': 0",value:0,color:"#ededed"}','tn'=>'{id:"TN",balloonText:"'.__('c-tn','wp-slimstat').': 0",value:0,color:"#ededed"}','tr'=>'{id:"TR",balloonText:"'.__('c-tr','wp-slimstat').': 0",value:0,color:"#ededed"}','tm'=>'{id:"TM",balloonText:"'.__('c-tm','wp-slimstat').': 0",value:0,color:"#ededed"}','tc'=>'{id:"TC",balloonText:"'.__('c-tc','wp-slimstat').': 0",value:0,color:"#ededed"}','ug'=>'{id:"UG",balloonText:"'.__('c-ug','wp-slimstat').': 0",value:0,color:"#ededed"}','ua'=>'{id:"UA",balloonText:"'.__('c-ua','wp-slimstat').': 0",value:0,color:"#ededed"}','ae'=>'{id:"AE",balloonText:"'.__('c-ae','wp-slimstat').': 0",value:0,color:"#ededed"}','gb'=>'{id:"GB",balloonText:"'.__('c-gb','wp-slimstat').': 0",value:0,color:"#ededed"}','us'=>'{id:"US",balloonText:"'.__('c-us','wp-slimstat').': 0",value:0,color:"#ededed"}','uy'=>'{id:"UY",balloonText:"'.__('c-uy','wp-slimstat').': 0",value:0,color:"#ededed"}','uz'=>'{id:"UZ",balloonText:"'.__('c-uz','wp-slimstat').': 0",value:0,color:"#ededed"}','vu'=>'{id:"VU",balloonText:"'.__('c-vu','wp-slimstat').': 0",value:0,color:"#ededed"}','ve'=>'{id:"VE",balloonText:"'.__('c-ve','wp-slimstat').': 0",value:0,color:"#ededed"}','vn'=>'{id:"VN",balloonText:"'.__('c-vn','wp-slimstat').': 0",value:0,color:"#ededed"}','vg'=>'{id:"VG",balloonText:"'.__('c-vg','wp-slimstat').': 0",value:0,color:"#ededed"}','vi'=>'{id:"VI",balloonText:"'.__('c-vi','wp-slimstat').': 0",value:0,color:"#ededed"}','eh'=>'{id:"EH",balloonText:"'.__('c-eh','wp-slimstat').': 0",value:0,color:"#ededed"}','ye'=>'{id:"YE",balloonText:"'.__('c-ye','wp-slimstat').': 0",value:0,color:"#ededed"}','zm'=>'{id:"ZM",balloonText:"'.__('c-zm','wp-slimstat').': 0",value:0,color:"#ededed"}','zw'=>'{id:"ZW",balloonText:"'.__('c-zw','wp-slimstat').': 0",value:0,color:"#ededed"}','gg'=>'{id:"GG",balloonText:"'.__('c-gg','wp-slimstat').': 0",value:0,color:"#ededed"}','je'=>'{id:"JE",balloonText:"'.__('c-je','wp-slimstat').': 0",value:0,color:"#ededed"}','im'=>'{id:"IM",balloonText:"'.__('c-im','wp-slimstat').': 0",value:0,color:"#ededed"}','mv'=>'{id:"MV",balloonText:"'.__('c-mv','wp-slimstat').': 0",value:0,color:"#ededed"}');
1845
+ $countries_not_represented = array( __( 'c-eu', 'wp-slimstat' ) );
1846
+ $max = 0;
1847
+
1848
+ foreach ( $countries as $a_country ) {
1849
+ if ( !array_key_exists( $a_country[ 'country' ], $data_areas ) ) {
1850
+ continue;
1851
+ }
1852
+
1853
+ $percentage = ( self::$pageviews > 0 ) ? sprintf( "%01.2f", ( 100 * $a_country[ 'counthits' ] / self::$pageviews ) ) : 0;
1854
+ $percentage_format = number_format( $percentage, 2, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] );
1855
+ $balloon_text = __( 'c-' . $a_country[ 'country' ], 'wp-slimstat' ) . ': ' . $percentage_format . '% (' . number_format( $a_country[ 'counthits' ], 0, wp_slimstat_db::$formats[ 'decimal' ], wp_slimstat_db::$formats[ 'thousand' ] ) . ')';
1856
+ $data_areas[ $a_country[ 'country' ] ] = '{id:"' . strtoupper( $a_country[ 'country' ] ) . '",balloonText:"' . $balloon_text . '",value:' . $percentage . '}';
1857
+
1858
+ if ( $percentage > $max ) {
1859
+ $max = $percentage;
1860
+ }
1861
+ }
1862
+
1863
+ $path_slimstat = dirname( dirname( __FILE__ ) );
1864
+ wp_enqueue_script( 'slimstat_ammap', plugins_url( '/admin/js/ammap/ammap.js', $path_slimstat ), array(), null, false );
1865
+ wp_enqueue_script( 'slimstat_ammap_world', plugins_url( '/admin/js/ammap/world.js', $path_slimstat ), array(), null, false );
1866
+ wp_enqueue_script( 'slimstat_amcharts_plugins_export', plugins_url( '/admin/js/amcharts/plugins/export/export.min.js', $path_slimstat ), array( 'slimstat_ammap' ), null, false );
1867
+ wp_enqueue_script( 'slimstat_amcharts_theme_light', plugins_url( '/admin/js/amcharts/themes/light.js', $path_slimstat ), array( 'slimstat_ammap' ), null, false );
1868
+
1869
+ wp_enqueue_style( 'slimstat_amcharts_plugins_export_css', plugins_url( '/admin/js/amcharts/plugins/export/export.css', $path_slimstat ) );
1870
+
1871
+ ?>
1872
+
1873
+ <div id="map_slim_p6_01" style="height: 100%"></div>
1874
+
1875
+ <script type="text/javascript">
1876
+ jQuery( function() {
1877
+ var targetSVG = "M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z";
1878
+
1879
+ var dataProvider = {
1880
+ map: "worldLow",
1881
+ getAreasFromMap: true,
1882
+ areas:[ <?php echo implode( ',', $data_areas ) ?> ],
1883
+ images: [ <?php if ( !empty( $data_points ) ) echo implode( ',', $data_points ) ?> ]
1884
+ };
1885
+
1886
+ // Create AmMap object
1887
+ var map = new AmCharts.AmMap();
1888
+
1889
+ <?php if ($max != 0): ?>
1890
+ var legend = new AmCharts.ValueLegend();
1891
+ legend.height = 20;
1892
+ legend.minValue = "0.01";
1893
+ legend.maxValue = "<?php echo $max ?>%";
1894
+ legend.right = 20;
1895
+ legend.showAsGradient = true;
1896
+ legend.width = 300;
1897
+ map.valueLegend = legend;
1898
+ <?php endif; ?>
1899
+
1900
+ // Configuration
1901
+ map.areasSettings = {
1902
+ autoZoom: false,
1903
+ color: "#9dff98",
1904
+ colorSolid: "#fa8a50",
1905
+ outlineColor: "#888888",
1906
+ selectedColor: "#ffb739"
1907
+ };
1908
+ map.imagesSettings = {
1909
+ rollOverColor: "#089282",
1910
+ rollOverScale: 2,
1911
+ selectedScale: 2,
1912
+ selectedColor: "#089282",
1913
+ color: "#13564e"
1914
+ };
1915
+ map.zoomControl = {
1916
+ zoomControlEnabled: true,
1917
+ zoomFactor: 3
1918
+ };
1919
+ map.export = {
1920
+ "enabled": true,
1921
+ "libs": {
1922
+ "path": "<?php echo plugins_url('/js/amcharts/plugins/export/libs/', dirname(__FILE__)) ?>"
1923
+ },
1924
+ "menu": [ {
1925
+ "class": "export-main",
1926
+ "menu": [ {
1927
+ "label": "Download as...",
1928
+ "menu": [ "PNG", "PDF" ]
1929
+ } ]
1930
+ } ]
1931
+ };
1932
+ map.backgroundAlpha = .9;
1933
+ map.backgroundColor = "#7adafd";
1934
+ map.backgroundZoomsToTop = false;
1935
+ map.balloon.color = "#000000";
1936
+ map.colorSteps = 5;
1937
+ map.mouseWheelZoomEnabled = true;
1938
+ map.pathToImages = "<?php echo plugins_url('/js/ammap/images/', dirname(__FILE__)) ?>";
1939
+
1940
+ // Init Data
1941
+ map.dataProvider = dataProvider;
1942
+
1943
+ // Display Map
1944
+ map.write( "map_slim_p6_01" );
1945
+ });
1946
+ </script><?php
1947
+
1948
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1949
+ die();
1950
+ }
1951
+ }
1952
+
1953
+ public static function get_search_terms_info( $_searchterms = '', $_referer = '', $_serp_only = false ) {
1954
+ $query_details = '';
1955
+ $search_terms_info = '';
1956
+ parse_str( $_referer, $query_parse_str );
1957
+
1958
+ if ( !empty( $query_parse_str[ 'source' ] ) && !$_serp_only ) {
1959
+ $query_details = __( 'src', 'wp-slimstat' ) . ": {$query_parse_str[ 'source' ]}";
1960
+ }
1961
+
1962
+ if ( !empty( $query_parse_str[ 'cd' ] ) ) {
1963
+ $query_details = __( 'serp', 'wp-slimstat') . ": {$query_parse_str[ 'cd' ]}";
1964
+ }
1965
+
1966
+ if ( !empty( $query_details ) ) {
1967
+ $query_details = "($query_details)";
1968
+ }
1969
+
1970
+ if ( !empty( $_searchterms ) && $_searchterms != '_' ) {
1971
+ $search_terms_info = '<a class="slimstat-font-logout" target="_blank" title="' . htmlentities( __( 'Go to the referring page', 'wp-slimstat' ), ENT_QUOTES, 'UTF-8' ) . '" href="' . $_referer . '"></a>' . htmlentities( $_searchterms, ENT_QUOTES, 'UTF-8' );
1972
+ $search_terms_info = "$search_terms_info $query_details";
1973
+ }
1974
+ return $search_terms_info;
1975
+ }
1976
+
1977
+ /**
1978
+ * Generate the HTML that lists all the filters currently used
1979
+ */
1980
+ public static function get_filters_html( $_filters_array = array() ) {
1981
+ $filters_html = '';
1982
+
1983
+ if ( !empty( $_filters_array ) ) {
1984
+ foreach( $_filters_array as $a_filter_label => $a_filter_details ) {
1985
+ if ( !array_key_exists( $a_filter_label, wp_slimstat_db::$columns_names ) || strpos( $a_filter_label, 'no_filter' ) !== false ) {
1986
+ continue;
1987
+ }
1988
+
1989
+ $a_filter_value_no_slashes = htmlentities( str_replace( '\\','', $a_filter_details[ 1 ] ), ENT_QUOTES, 'UTF-8' );
1990
+ $filters_html .= '<li>' . strtolower( wp_slimstat_db::$columns_names[ $a_filter_label ][ 0 ] ) . ' ' . __( str_replace( '_', ' ', $a_filter_details[ 0 ] ), 'wp-slimstat' ) . " $a_filter_value_no_slashes <a class='slimstat-filter-link slimstat-font-cancel' title='" . htmlentities( __( 'Remove filter for', 'wp-slimstat' ), ENT_QUOTES, 'UTF-8' ) . ' ' . wp_slimstat_db::$columns_names[ $a_filter_label ][ 0 ] . "' href='" . self::fs_url( "$a_filter_label equals " ) . "'></a></li>";
1991
+ }
1992
+ }
1993
+
1994
+ if ( !empty( $filters_html ) ) {
1995
+ $filters_html = "<ul class='slimstat-filter-list'>$filters_html</ul><a href='#' id='slimstat-save-filter' class='slimstat-filter-action-button button-secondary noslimstat' data-filter-array='" . htmlentities( json_encode( $_filters_array ), ENT_QUOTES, 'UTF-8' ) . "'>" . __( 'Save', 'wp-slimstat' ) . '</a>';
1996
+ }
1997
+
1998
+ if ( count( $_filters_array ) > 1 ) {
1999
+ $filters_html .= '<a href="' . self::fs_url() . '" id="slimstat-remove-all-filters" class="button-secondary slimstat-filter-action-button noslimstat">' . __( 'Reset All', 'wp-slimstat' ) . '</a>';
2000
+ }
2001
+
2002
+ return $filters_html;
2003
+ }
2004
+
2005
+ public static function fs_url( $_filters_string = '' ) {
2006
+ // Allow only legitimate requests
2007
+ if ( !is_admin() ) {
2008
+ return '';
2009
+ }
2010
+
2011
+ $request_uri = admin_url( 'admin.php' );
2012
+
2013
+ $request_page = 'slimview1';
2014
+ if ( !empty( $_REQUEST[ 'page' ] ) ) {
2015
+ if ( !array_key_exists( $_REQUEST[ 'page' ], wp_slimstat_admin::$screens_info ) ) {
2016
+ return '';
2017
+ }
2018
+
2019
+ $request_page = $_REQUEST[ 'page' ];
2020
+ }
2021
+
2022
+ $request_uri .= '?page=' . $request_page;
2023
+
2024
+ // Avoid XSS attacks ( why would the owner try to hack into his/her own website though? )
2025
+ if ( !empty( $_SERVER[ 'HTTP_REFERER' ] ) ) {
2026
+ $parsed_referer = parse_url( $_SERVER[ 'HTTP_REFERER' ] );
2027
+ if ( !$parsed_referer || ( !empty( $parsed_referer[ 'scheme' ] ) && !in_array( strtolower( $parsed_referer[ 'scheme' ] ), array( 'http', 'https' ) ) ) ) {
2028
+ return '';
2029
+ }
2030
+ }
2031
+
2032
+ $filters_normalized = wp_slimstat_db::parse_filters( $_filters_string );
2033
+
2034
+ // Columns
2035
+ if ( !empty( $filters_normalized[ 'columns' ] ) ) {
2036
+ foreach ( $filters_normalized[ 'columns' ] as $a_key => $a_filter ) {
2037
+ $a_key = str_replace( '_calculated', '', $a_key );
2038
+ $request_uri .= "&amp;fs%5B$a_key%5D=" . urlencode( $a_filter[ 0 ] . ' ' . $a_filter[ 1 ] );
2039
+ }
2040
+ }
2041
+
2042
+ // Date ranges
2043
+ if ( !empty( $filters_normalized[ 'date' ] ) ) {
2044
+ foreach ( $filters_normalized[ 'date' ] as $a_key => $a_filter ) {
2045
+ if ( isset( $a_filter ) ) {
2046
+ $request_uri .= "&amp;fs%5B$a_key%5D=" . urlencode( 'equals ' . $a_filter );
2047
+ }
2048
+ }
2049
+ }
2050
+
2051
+ // Misc filters
2052
+ if ( !empty( $filters_normalized[ 'misc' ] ) ) {
2053
+ foreach ( $filters_normalized[ 'misc' ] as $a_key => $a_filter ) {
2054
+ $request_uri .= "&amp;fs%5B$a_key%5D=" . urlencode( 'equals ' . $a_filter );
2055
+ }
2056
+ }
2057
+
2058
+ return $request_uri;
2059
+ }
2060
+
2061
+ /**
2062
+ * Attempts to convert a permalink into a post title
2063
+ */
2064
+ public static function get_resource_title( $_resource = '' ) {
2065
+ $resource_title = $_resource;
2066
+
2067
+ if ( wp_slimstat::$settings[ 'convert_resource_urls_to_titles' ] != 'on' ) {
2068
+ return htmlentities( urldecode( $resource_title ), ENT_QUOTES, 'UTF-8' );
2069
+ }
2070
+
2071
+ // Is this a post or a page?
2072
+ $post_id = url_to_postid( $_resource );
2073
+
2074
+ if ( $post_id > 0 ) {
2075
+ $resource_title = the_title_attribute( array( 'post' => $post_id, 'echo' => false ) );
2076
+
2077
+ // Encode URLs to avoid XSS attacks
2078
+ if ( $resource_title == $_resource ) {
2079
+ $resource_title = htmlspecialchars( $resource_title, ENT_QUOTES, 'UTF-8' );
2080
+ }
2081
+ }
2082
+
2083
+ // Is this a category or tag permalink?
2084
+ else {
2085
+ $term_names = array();
2086
+ $home_url = get_home_url();
2087
+ $relative_home = parse_url( $home_url, PHP_URL_PATH );
2088
+
2089
+ $all_terms = get_terms( 'category' );
2090
+ foreach ( $all_terms as $a_term ) {
2091
+ $term_link = get_term_link( $a_term, 'category' );
2092
+ if ( !is_wp_error( $term_link ) && str_replace( $home_url, $relative_home, $term_link ) == $_resource ) {
2093
+ $term_names[] = $a_term->name;
2094
+ }
2095
+ }
2096
+
2097
+ $all_terms = get_terms( 'tag' );
2098
+ foreach ( $all_terms as $a_term ) {
2099
+ $term_link = get_term_link( $a_term, 'tag' );
2100
+ if ( !is_wp_error( $term_link ) && str_replace( $home_url, $relative_home, $term_link ) == $_resource ) {
2101
+ $term_names[] = $a_term->name;
2102
+ }
2103
+ }
2104
+
2105
+ if ( !empty( $term_names ) ) {
2106
+ $resource_title = implode( ',', $term_names );
2107
+ }
2108
+ else {
2109
+ $resource_title = htmlspecialchars( $resource_title, ENT_QUOTES, 'UTF-8' );
2110
+ }
2111
+ }
2112
+
2113
+ return $resource_title;
2114
+ }
2115
+
2116
+ public static function inline_help( $_text = '', $_echo = true ) {
2117
+ if ( is_admin() && !empty( $_text ) ) {
2118
+ $wrapped_text = "<i class='slimstat-tooltip-trigger corner'><span class='slimstat-tooltip-content'>$_text</span></i>";
2119
+ }
2120
+ else {
2121
+ $wrapped_text = '';
2122
+ }
2123
+
2124
+ if ($_echo)
2125
+ echo $wrapped_text;
2126
+ else
2127
+ return $wrapped_text;
2128
+ }
2129
+
2130
+ protected static function _check_args( $_args = array() ) {
2131
+
2132
+ // When called from the WP Dashboard, the action passes 2 arguments: post_id (empty) and array of arguments
2133
+ if ( !empty( $_args[ 1 ] ) ) {
2134
+ $_args = $_args[ 1 ];
2135
+ }
2136
+ // When called from within Slimstat, the first argument is the array of arguments for the callback
2137
+ else {
2138
+ $_args = $_args[ 0 ];
2139
+ }
2140
+
2141
+ if ( !is_array( $_args ) ) {
2142
+ $_args = array();
2143
+ }
2144
+
2145
+ $report_id = 0;
2146
+
2147
+ // Is this an Ajax request?
2148
+ if ( !empty( $_POST[ 'report_id' ] ) ) {
2149
+ check_ajax_referer( 'meta-box-order', 'security' ); // Let's make sure the request is coming from an authorized source
2150
+ $report_id = $_POST[ 'report_id' ];
2151
+ }
2152
+
2153
+ // When on the WP Dashboard, the action sets the 'id' key of the $_args array
2154
+ else if ( !empty( $_args[ 'id' ] ) ){
2155
+ $report_id = $_args[ 'id' ];
2156
+ }
2157
+
2158
+ // Honor the 'hidden' attribute, but not on the WP Dashboard ( empty( $_args[ 'id' ] ) )
2159
+ // if ( empty( $report_id ) || in_array( 'hidden', self::$reports_info[ $report_id ][ 'classes' ] ) ) {
2160
+ // return array();
2161
+ // }
2162
+
2163
+ if ( !empty( self::$reports_info[ $report_id ] ) && is_array( self::$reports_info[ $report_id ] ) ) {
2164
+ // Default values
2165
+ $_args = array_merge( array(
2166
+ 'title' => '',
2167
+ 'callback' => '',
2168
+ 'callback_args' => array(),
2169
+ 'classes' => array(),
2170
+ 'screens' => array(),
2171
+ 'tooltip' => ''
2172
+ ), self::$reports_info[ $report_id ] );
2173
+ }
2174
+
2175
+ // Default callback args
2176
+ $_args[ 'callback_args' ] = array_merge( array(
2177
+ 'type' => '',
2178
+ 'columns' => '',
2179
+ 'where' => '',
2180
+ 'having' => '',
2181
+ 'as_column' => '',
2182
+ 'filter_op' => 'equals',
2183
+ 'outer_select_column' => '',
2184
+ 'aggr_function' => 'MAX',
2185
+ 'use_date_filters' => true,
2186
+ 'results_per_page' => wp_slimstat::$settings[ 'rows_to_show' ],
2187
+ 'criteria' => ''
2188
+ ), $_args[ 'callback_args' ] );
2189
+
2190
+ return $_args;
2191
+ }
2192
+ }
 
 
 
 
 
admin/wp-slimstat-admin.php CHANGED
@@ -11,7 +11,7 @@ class wp_slimstat_admin {
11
  * Init -- Sets things up.
12
  */
13
  public static function init() {
14
- self::$admin_notice = "We would like to thank all of those who replied to our appeal for help. It really meant a lot to us. In order to provide a way to contribute updates and bugfixes, we setup a <a href='https://github.com/slimstat/wp-slimstat' target='_blank'>public Github repository</a>, where you can submit your pull requests and point out bugs and other issues. If you would like Slimstat to speak your language (or to extend any existing partial localization), please do not hesitate to submit your language files either via Github or directly to our support team. In the meanwhile, we will keep working on our documentation, videos and on polishing our code even more. For example, this release includes a completely rewritten Javascript library to handle all the features available in the admin, from the Filter Bar to the Customizer. Let us know if you notice anything out of the ordinary, and feel free to submit a pull request to fix any bugs you might find.";
15
  self::$admin_notice .= '<br/><br/><a id="slimstat-hide-admin-notice" href="#" class="button-secondary">Got it, thanks</a>';
16
 
17
  // Load language files
@@ -691,7 +691,7 @@ class wp_slimstat_admin {
691
 
692
  $parsed_permalink = parse_url( get_permalink( $_post_id ) );
693
  $parsed_permalink = $parsed_permalink[ 'path' ] . ( !empty( $parsed_permalink[ 'query' ] ) ? '?' . $parsed_permalink[ 'query' ] : '' );
694
- wp_slimstat_db::init( 'resource contains ' . $parsed_permalink . '&&&interval equals ' . wp_slimstat::$settings[ 'posts_column_day_interval' ] . '&&&interval_direction equals minus' );
695
 
696
  if ( wp_slimstat::$settings[ 'posts_column_pageviews' ] == 'on' ) {
697
  $count = wp_slimstat_db::count_records();
@@ -699,7 +699,7 @@ class wp_slimstat_admin {
699
  else{
700
  $count = wp_slimstat_db::count_records( 'ip' );
701
  }
702
- echo '<a href="'.wp_slimstat_reports::fs_url( 'resource contains ' . $parsed_permalink . '&&&interval equals ' . wp_slimstat::$settings[ 'posts_column_day_interval' ] . '&&&interval_direction equals minus' ). '">'.$count.'</a>';
703
  }
704
  // end add_column
705
 
11
  * Init -- Sets things up.
12
  */
13
  public static function init() {
14
+ self::$admin_notice = "We've completely rewritten the portion of code that handles the date ranges in the Filter Bar. In order to simplify things, <strong>we have deprecated</strong> the shortcode filter <code>interval_direction</code>, which is now expressed by the sign in front of the interval value (positive for going forward from a given start date, and negative for going back in time). Please note that this change affect your existing shortcodes, if they use the aforementioned filter. We will update our documentation in the next few days to remove any reference to this filter, and to avoid any confusion. We've also reintroduced the various levels of granularity for our charts: hourly (when a single day is selected), daily (for ranges up to 120 days) and monthly. Last but not least, the comparison chart is now <strong>always</strong> displayed, using new criteria to determine the range to use. You may want to change your settings (Settings > Reports > Default Time Span > Days, and Reports > Comparison Chart) to mimic the old behavior or hide the comparison chart altogether, if you like. Please feel free to contact us if you have any questions or to report any issues.";
15
  self::$admin_notice .= '<br/><br/><a id="slimstat-hide-admin-notice" href="#" class="button-secondary">Got it, thanks</a>';
16
 
17
  // Load language files
691
 
692
  $parsed_permalink = parse_url( get_permalink( $_post_id ) );
693
  $parsed_permalink = $parsed_permalink[ 'path' ] . ( !empty( $parsed_permalink[ 'query' ] ) ? '?' . $parsed_permalink[ 'query' ] : '' );
694
+ wp_slimstat_db::init( 'resource contains ' . $parsed_permalink . '&&&interval equals -' . wp_slimstat::$settings[ 'posts_column_day_interval' ] );
695
 
696
  if ( wp_slimstat::$settings[ 'posts_column_pageviews' ] == 'on' ) {
697
  $count = wp_slimstat_db::count_records();
699
  else{
700
  $count = wp_slimstat_db::count_records( 'ip' );
701
  }
702
+ echo '<a href="'.wp_slimstat_reports::fs_url( 'resource contains ' . $parsed_permalink . '&&&interval equals -' . wp_slimstat::$settings[ 'posts_column_day_interval' ] ). '">'.$count.'</a>';
703
  }
704
  // end add_column
705
 
readme.txt CHANGED
@@ -1,155 +1,162 @@
1
- === Slimstat Analytics ===
2
- Contributors: coolmann
3
- Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BNJR5EZNY3W38
4
- Tags: analytics, statistics, counter, tracking, reports, wassup, geolocation, online users, spider, tracker, pageviews, stats, maxmind, statistics, statpress, power stats, hit
5
- Text Domain: wp-slimstat
6
- Requires at least: 3.8
7
- Tested up to: 4.9
8
- Stable tag: 4.7.6.1
9
-
10
- == Description ==
11
- The leading web analytics plugin for WordPress. Track returning customers and registered users, monitor Javascript events, detect intrusions, analyze email campaigns. Thousands of WordPress sites are already using it.
12
-
13
- = Feature Spotlight =
14
- [youtube https://www.youtube.com/watch?v=zEKP9yC8x6g]
15
-
16
- = Main features =
17
- * Get access to real-time access log, measure server latency, track page events, keep an eye on your bounce rate and much more.
18
- * Add shortcodes to your website to display reports in widgets or directly in posts and pages.
19
- * Exclude users from statistics collection based on various criteria, including; user roles, common robots, IP subnets, admin pages, country, etc.
20
- * Export your reports to CSV, generate user heatmaps or get daily emails right in your mailbox (via premium add-ons).
21
- * Compatible with W3 Total Cache, WP SuperCache, CloudFlare and most caching plugins.
22
- * Support for hashing IP addresses in the database to protect your users privacy.
23
- * Accurate IP geolocation, browser and platform detection (courtesy of [MaxMind](http://www.maxmind.com/) and [Browscap](http://browscap.org)).
24
- * World Map that works on your mobile device, too (courtesy of [amMap](http://www.ammap.com/)).
25
-
26
- = Premium Add-ons =
27
- Visit [our website](http://www.wp-slimstat.com/addons/) for a list of available extensions.
28
-
29
- = Social Media =
30
- [Like Us](https://www.facebook.com/slimstatistics/) on Facebook and [follow us](https://twitter.com/wp_stats) on Twitter to get the latest news and updates about our plugin.
31
-
32
- = Contribute =
33
- Slimstat Analytics is an open source project, dependent in large parts on community support. You can fork our [Github repository](https://github.com/slimstat/wp-slimstat) and submit code enhancements, bugfixes or provide localization files to let our plugin speak even more languages. [This page](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BNJR5EZNY3W38)
34
- is for those who would like to donate money - be it once, be it regularly, be it a small or a big amount. Everything is set up for an easy donation process.
35
- Try it out, you'll be amazed how good it feels! If you're on a tight budget, and coding is not your thing, please consider writing [a review](https://wordpress.org/support/plugin/wp-slimstat/reviews/#new-post) for Slimstat as a token of appreciation for our hard work!
36
-
37
- = Translations =
38
- Slimstat is available in multiple languages: English, Belarusian (UStarCash), Chinese (沐熙工作室), Farsi, French (Michael Bastin, Jean-Michel Venet, Yves Pouplard, Henrick Kac), German (TechnoViel), Indonesian ([ChameleonJohn](https://www.chameleonjohn.com/)), Italian ([Slimstat Dev Team](https://www.wp-slimstat.com)), Japanese (h_a_l_f), Portuguese, Russian (Vitaly), Spanish ([WebHostingHub](http://www.webhostinghub.com/)), Swedish (Per Soderman) and Turkish (Seyit Mehmet Çoban). Is your language missing or incomplete? [Contact us](http://support.wp-slimstat.com/) today.
39
-
40
- = Reviews and Feedback =
41
- * This is by far the most accurate and in-depth tracking plugin I've encountered for WordPress [MiMango](https://wordpress.org/support/topic/excellent-plugin-and-service-9)
42
- * I have been relying on SlimStat to not only track all traffic to my sites accurately but also to present the stats in very useful graphic format [JJD3](https://wordpress.org/support/topic/an-essential-plugin-14)
43
- * 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)
44
- * 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)
45
- * Read all the [reviews](http://wordpress.org/support/view/plugin-reviews/wp-slimstat) and feel free to post your own!
46
-
47
- = Requirements =
48
- * WordPress 3.8+
49
- * PHP 5.2+ (or 5.5+ if you use the Browscap data file)
50
- * MySQL 5.0.3+
51
- * At least 20 MB of free web space
52
- * At least 5 MB of free DB space
53
- * At least 32 Mb of free PHP memory for the tracker (peak memory usage)
54
- * IE9+ or any browser supporting HTML5, to access the reports
55
-
56
- == Installation ==
57
- 1. In your WordPress admin, go to Plugins > Add New
58
- 2. Search for WP Slimstat Analytics
59
- 3. Click on **Install Now** under WP Slimstat Analytics and then activate the plugin
60
- 4. Make sure your template calls `wp_footer()` or the equivalent hook somewhere (possibly just before the `</body>` tag)
61
- 5. Go to Slimstat > Settings > Maintenance tab > MaxMind IP to Country section and click on "Install GeoLite DB" to detect your visitors' countries based on their IP addresses
62
- 6. If your `wp-admin` folder is not publicly accessible, make sure to check the [FAQs](http://wordpress.org/extend/plugins/wp-slimstat/faq/) to see if there's anything else you need to do
63
-
64
- == Please note ==
65
- * If you decide to uninstall Slimstat Analytics, all the stats will be **PERMANENTLY** deleted from your database. Make sure to setup a database backup (wp_slim_*) to avoid losing your data.
66
- * If you are upgrading from a version prior to 4.0, please install version 4.0 first to upgrade the database structure and download the new Geolocation data.
67
-
68
- == Frequently Asked Questions ==
69
- Our knowledge base is available on our [support center](http://docs.wp-slimstat.com/) website.
70
-
71
- == Screenshots ==
72
- 1. **Overview** - Your website traffic at a glance
73
- 2. **Activity Log** - A real-time view of your visitors' whereabouts
74
- 3. **Settings** - Plenty of options to customize the plugin's behavior
75
- 4. **Interactive World Map** - See where your visitors are coming from
76
- 5. **Responsive layout** - Keep an eye on your reports on the go
77
-
78
- == Changelog ==
79
- = 4.7.6.1 =
80
- * [Fix] The new Javascript library was interfering with the dropdown menus on the WordPress Dashboard. Thanks to all of those who helped us troubleshoot the issue.
81
-
82
- = 4.7.6 =
83
- * [Note] As we mentioned earlier, we've been working on streamlining and cleaning up our source code. It's incredible how layers of code can deposit on top of each other, until they form a thick layer that prevents developers from seeing clearly what's happening. It was time to apply some virtual citric acid to descale our code. If you're using any kind of server caching functionality, please make sure to clear your cache before opening a support request. Also, do not hesitate to reach out to us if you notice any strange or unusual behaviors.
84
- * [New] You can now download the geolocation map as a PDF file or PNG image. Thank you, Steve, for suggesting this feature!
85
- * [New] You can now exclude pageviews by content type, like 404, taxonomy, search, etc. Thank you, [victor50g](https://wordpress.org/support/topic/suggestion-enable-filtering-on-content-404/).
86
- * [Update] Deprecated the 'direction' filter, which was a leftover from over 5 years ago, when we used our old interface.
87
- * [Fix] The cron job to archive old records was not firing as expected under certain circumstances.
88
- * [Fix] Charts did not render correctly, if no data was available for that specific view.
89
-
90
- = 4.7.5.3 =
91
- * [New] For those who can't manage to automatically update their local copy of our premium add-ons, we've added a link to manually download the zip file. You'll find it under the Plugins screen, once you've entered and saved the corresponding license key under Slimstat > Add-ons.
92
- * [Update] The [Update Checker](https://github.com/YahnisElsts/plugin-update-checker) library has been updated to version 4.4.
93
- * [Fix] The world map was not being displayed correctly if no data points were available for the selected time range (thank you, Michel).
94
-
95
- = 4.7.5.2 =
96
- * [Update] You can now customize the amount of dots displayed on the World Map, under Slimstat > Settings > Reports > Access Log and World Map. Thank you, [service4](https://wordpress.org/support/topic/new-geolocation-map-with-cities/).
97
- * [Fix] A dependency error was being highlighted for one of our premium add-ons under certain circumstances. Thank you, Peter.
98
- * [Fix] The option to not set the session cookie was not working as expected. Thank you, [Bjarne](https://wordpress.org/support/topic/disable-cookies-2/#post-9887099).
99
-
100
- = 4.7.5.1 =
101
- * [Update] Implemented a workaround to try and fix the "Forbidden" error that a few users are experiencing when trying to download the MaxMind Geolite2 data file.
102
- * [Fix] Updated the link to manually download the MaxMind data file from their servers, and added a new page to our knowledge base to explain how to manually install it.
103
-
104
- = 4.7.5 =
105
- * [New] Now that Slimstat is capable of geolocating visitors at the city level, wouldn't it make sense to display those visitors on the map? Well, of course! Go check out this new feature by accessing the Geolocation tab in Slimstat.
106
- * [New] Updated the tracking script to handle events triggered by external libraries, like the [Vimeo API](https://github.com/vimeo/player.js/#events). Thank you, Max.
107
- * [New] Added new operator "included_in_set", which allows you to list multiple values to match against, when composing a shortcode.
108
- * [New] Added new option to avoid that Slimstat assigns a COOKIE to your visitors. Thank you, [dragon013](https://wordpress.org/support/topic/disable-cookies-2/).
109
- * [Fix] A bug was preventing the feature to "restrict users" to only see their reports from working as expected.
110
-
111
- = 4.7.4.1 =
112
- * [Update] The Browscap data file is now loaded only when needed, thus removing its inherent overhead when unnecessary.
113
- * [Update] The Browscap data file has been updated to the latest version available on their repository (ver 6026).
114
- * [Fix] Addressed a remote XSS Vulnerability responsibly disclosed by one of our customers. Thank you, [riscybusiness](https://wordpress.org/support/topic/security-vulnerability-affecting-slimstat/).
115
- * [Fix] Reintroduced the WHOIS pin, which has been removed by mistake because of a regression bug. Thank you, [brachialis](https://wordpress.org/support/topic/whois-location-icon-disappear/).
116
-
117
- = 4.7.4 =
118
- * [Update] New fields added to the Email Report and Export to Excel add-ons, by extending how certain reports are defined in core.
119
- * [Fix] The [false positive](https://www.virustotal.com/#/file/43f69d9c4028f857b5b5544ea4559c03b4d58e02d75617482db517c626164363/detection) alert related to a virus in our code was fixed by updating [AmChart](https://www.amcharts.com/) to the latest version available (thank you, Sasa).
120
- * [Fix] Removed a PHP warning of undefined index (thank you, [slewis1000 and Sasa](https://wordpress.org/support/topic/php-notice-undefined-index-country/))
121
- * [Fix] The MozScape report was causing connectivity issues for some users, and it is now set as "hidden" by default.
122
- * [Fix] Regression bug related to our Export to Excel add-on.
123
-
124
- = 4.7.3.1 =
125
- * [Fix] Apparently more people than we initially thought have issues with the MaxMind data file not being saved as expected. We are introducing a temporary fix while we try to investigate this issue further.
126
-
127
- = 4.7.3 =
128
- * [Fix] A [few users](https://wordpress.org/support/topic/cannot-install-maxmind-geolite-db/) pointed out a weird behavior when installing the MaxMind Geolocation data file, where an empty folder would be created instead of the actual file. If you still experience issues related to this problem, please make sure to delete the empty folder "maxmind.mmdb" under `wp-content/uploads/wp-slimstat/`.
129
- * [Fix] Apparently Microsoft Security Essentials [was not pleased with our code](https://wordpress.org/support/topic/trojandownloader097m-donoff-detected-in-archive/), and was returning a false positive alert that a virus was included with the source code (thank you, Sasa).
130
- * [Fix] The "content_id" filter could not be used in a shortcode to reference other pages (i.e. `[slimstat f='count-all' w='id']content_id equals 2012[/slimstat]`). Thank you, Felipe.
131
- * [Fix] Country flags were not being displayed properly under certain circumstances (thank you, [Catmaniax](https://wordpress.org/support/topic/minor-issue-missing-png-file/)).
132
- * [Fix] Bug preventing the new Heatmap Add-on from working as expected.
133
-
134
- = 4.7.2.2 =
135
- * [New] Added support for SCRIPT_DEBUG: by defining this constant in your `wp-config.php` will make Slimstat load the unminified version of the javascript tracker (thank you, Sasa)
136
- * [Update] Added new parameter to make the `admin-ajax.php` URL relative, to solve issues like [this one](https://wordpress.org/support/topic/xmlhttprequest-cannot-load-wp-adminadmin-ajax-php-3/).
137
- * [Fix] The Network Settings premium add-on was not working because of a bug in the main plugin. Thank you, Steve, for pointing us into the right direction.
138
- * [Fix] Updated the schema (columns) for the archive table.
139
-
140
-
141
- = 4.7.2.1 =
142
- * [Fix] The new table columns "location" and "city" were not being created on a fresh install (thank you, [nielsgoossens](https://wordpress.org/support/topic/no-data-anymore-2/#post-9491034))
143
- * [Fix] Async mode was not working as expected (thank you, [keithgbcc](https://wordpress.org/support/topic/doesnt-work-1694/#post-9487448))
144
-
145
- = 4.7.2 =
146
- * [New] As those who have been using Slimstat for a while know, we never stop doing our good share of research and development to improve our plugin. One feature on our wishlist was to make the geolocation functionality more accurate. Specifically, users have been asking us to track not just the Country of origin, but possibly the state and city. In order to geolocate visitors, our code has been leveraging a third-party data file provided by [MaxMind.com](https://www.maxmind.com/en/home). A while ago, they launched a new data format, which improves performance and offers a way to quickly determine the city of origin. However, the new library required a higher version of PHP, and up until now we had been hesitant to adopt it, to allow more people to use our plugin, over the chance of offering this feature. Now, after spending some time combing through their code, we found a way to get the best of both worlds: by customizing their PHP library, we were able to make it work with PHP 5.3! Which means that now Slimstat is able to tell you your visitors' city of origin (and State, when applicable) right out of the box. This information is available in the Access Log report and in a new 'Top Cities' report under the Audience tab. Please note: the MaxMind data file to enable this feature is approximately 60 Mb, and for this reason <strong>this new functionality is not enabled by default</strong>. You must go to Slimstat > Settings > Tracker and turn on the corresponding option. Then go to Slimstat > Settings > Maintenance and uninstall/install the GeoLite file to download the one that contains the city data. Please feel free to contact us if you have any questions.
147
- * [Update] Removed backward compatibility code for those updating from a version prior to 4.2. Hopefully most of our users are using a newer version that that. If you're not, please contact our support service for instructions on how to upgrade.
148
- * [Update] The format used to save your settings in the database has been changed. You MUST update your premium add-ons as soon as possible, and get the version compatible with this new format, or you might notice unexpected behaviors. Please contact us if you experience difficulties updating your add-ons.
149
- * [Update] Cleaned up some old CSS code affecting the reports.
150
-
151
- = 4.7.1 =
152
- * [Fix] The new feature introduced in version 4.6.9.1 to allow our users to customize the default time range for the reports, had introduced a regression bug. Thank you to all our users who volunteered to test the bugfix.
153
- * [Fix] A vulnerability has been disclosed by [Pluginvulnerabilities.com](pluginvulnerabilities.com): an attacker with admin credentials could leverage the import/export mechanism for the plugin's settings to inject some malicious code. We recommend that you upgrade to the latest version of Slimstat as soon as possible.
154
- * [Fix] The new version of the [Add-on Update Checker library](https://github.com/YahnisElsts/plugin-update-checker), bundled with the previous release, was returning a fatal error under certain circumstances (thank you, Pepe).
155
-
 
 
 
 
 
 
 
1
+ === Slimstat Analytics ===
2
+ Contributors: coolmann
3
+ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BNJR5EZNY3W38
4
+ Tags: analytics, statistics, counter, tracking, reports, wassup, geolocation, online users, spider, tracker, pageviews, stats, maxmind, statistics, statpress, power stats, hit
5
+ Text Domain: wp-slimstat
6
+ Requires at least: 3.8
7
+ Requires PHP: 5.2
8
+ Tested up to: 4.9
9
+ Stable tag: 4.7.7
10
+
11
+ == Description ==
12
+ The leading web analytics plugin for WordPress. Track returning customers and registered users, monitor Javascript events, detect intrusions, analyze email campaigns. Thousands of WordPress sites are already using it.
13
+
14
+ = Feature Spotlight =
15
+ [youtube https://www.youtube.com/watch?v=zEKP9yC8x6g]
16
+
17
+ = Main features =
18
+ * Get access to real-time access log, measure server latency, track page events, keep an eye on your bounce rate and much more.
19
+ * Add shortcodes to your website to display reports in widgets or directly in posts and pages.
20
+ * Exclude users from statistics collection based on various criteria, including; user roles, common robots, IP subnets, admin pages, country, etc.
21
+ * Export your reports to CSV, generate user heatmaps or get daily emails right in your mailbox (via premium add-ons).
22
+ * Compatible with W3 Total Cache, WP SuperCache, CloudFlare and most caching plugins.
23
+ * Support for hashing IP addresses in the database to protect your users privacy.
24
+ * Accurate IP geolocation, browser and platform detection (courtesy of [MaxMind](http://www.maxmind.com/) and [Browscap](http://browscap.org)).
25
+ * World Map that works on your mobile device, too (courtesy of [amMap](http://www.ammap.com/)).
26
+
27
+ = Premium Add-ons =
28
+ Visit [our website](http://www.wp-slimstat.com/addons/) for a list of available extensions.
29
+
30
+ = Social Media =
31
+ [Like Us](https://www.facebook.com/slimstatistics/) on Facebook and [follow us](https://twitter.com/wp_stats) on Twitter to get the latest news and updates about our plugin.
32
+
33
+ = Contribute =
34
+ Slimstat Analytics is an open source project, dependent in large parts on community support. You can fork our [Github repository](https://github.com/slimstat/wp-slimstat) and submit code enhancements, bugfixes or provide localization files to let our plugin speak even more languages. [This page](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BNJR5EZNY3W38)
35
+ is for those who would like to donate money - be it once, be it regularly, be it a small or a big amount. Everything is set up for an easy donation process.
36
+ Try it out, you'll be amazed how good it feels! If you're on a tight budget, and coding is not your thing, please consider writing [a review](https://wordpress.org/support/plugin/wp-slimstat/reviews/#new-post) for Slimstat as a token of appreciation for our hard work!
37
+
38
+ = Translations =
39
+ Slimstat is available in multiple languages: English, Belarusian (UStarCash), Chinese (沐熙工作室), Farsi, French (Michael Bastin, Jean-Michel Venet, Yves Pouplard, Henrick Kac), German (TechnoViel), Indonesian ([ChameleonJohn](https://www.chameleonjohn.com/)), Italian ([Slimstat Dev Team](https://www.wp-slimstat.com)), Japanese (h_a_l_f), Portuguese, Russian (Vitaly), Spanish ([WebHostingHub](http://www.webhostinghub.com/)), Swedish (Per Soderman) and Turkish (Seyit Mehmet Çoban). Is your language missing or incomplete? [Contact us](http://support.wp-slimstat.com/) today.
40
+
41
+ = Reviews and Feedback =
42
+ * This is by far the most accurate and in-depth tracking plugin I've encountered for WordPress [MiMango](https://wordpress.org/support/topic/excellent-plugin-and-service-9)
43
+ * I have been relying on SlimStat to not only track all traffic to my sites accurately but also to present the stats in very useful graphic format [JJD3](https://wordpress.org/support/topic/an-essential-plugin-14)
44
+ * 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)
45
+ * 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)
46
+ * Read all the [reviews](http://wordpress.org/support/view/plugin-reviews/wp-slimstat) and feel free to post your own!
47
+
48
+ = Requirements =
49
+ * WordPress 3.8+
50
+ * PHP 5.2+ (or 5.5+ if you use the Browscap data file)
51
+ * MySQL 5.0.3+
52
+ * At least 20 MB of free web space
53
+ * At least 5 MB of free DB space
54
+ * At least 32 Mb of free PHP memory for the tracker (peak memory usage)
55
+ * IE9+ or any browser supporting HTML5, to access the reports
56
+
57
+ == Installation ==
58
+ 1. In your WordPress admin, go to Plugins > Add New
59
+ 2. Search for WP Slimstat Analytics
60
+ 3. Click on **Install Now** under WP Slimstat Analytics and then activate the plugin
61
+ 4. Make sure your template calls `wp_footer()` or the equivalent hook somewhere (possibly just before the `</body>` tag)
62
+ 5. Go to Slimstat > Settings > Maintenance tab > MaxMind IP to Country section and click on "Install GeoLite DB" to detect your visitors' countries based on their IP addresses
63
+ 6. If your `wp-admin` folder is not publicly accessible, make sure to check the [FAQs](http://wordpress.org/extend/plugins/wp-slimstat/faq/) to see if there's anything else you need to do
64
+
65
+ == Please note ==
66
+ * If you decide to uninstall Slimstat Analytics, all the stats will be **PERMANENTLY** deleted from your database. Make sure to setup a database backup (wp_slim_*) to avoid losing your data.
67
+ * If you are upgrading from a version prior to 4.0, please install version 4.0 first to upgrade the database structure and download the new Geolocation data.
68
+
69
+ == Frequently Asked Questions ==
70
+ Our knowledge base is available on our [support center](http://docs.wp-slimstat.com/) website.
71
+
72
+ == Screenshots ==
73
+ 1. **Overview** - Your website traffic at a glance
74
+ 2. **Activity Log** - A real-time view of your visitors' whereabouts
75
+ 3. **Settings** - Plenty of options to customize the plugin's behavior
76
+ 4. **Interactive World Map** - See where your visitors are coming from
77
+ 5. **Responsive layout** - Keep an eye on your reports on the go
78
+
79
+ == Changelog ==
80
+ = 4.7.7 =
81
+ * [New] We've completely rewritten the portion of code that handles the date ranges in the Filter Bar. In order to simplify things, **we have deprecated** the `interval_direction` filter, which is now expressed by the sign in front of the interval value (positive for going forward from a given start date, and negative for going back in time). Please note that this change affect your existing shortcodes, if they use the aforementioned filter. We will update our documentation in the next few days to remove any reference to this filter, and to avoid any confusion. Please feel free to contact us if you have any questions or to report any issues.
82
+ * [New] The comparison chart is now always displayed, using new criteria to determine the range to use. You may want to update your settings (Settings > Reports > Default Time Span > Days, and Reports > Comparison Chart) to mimic the old behavior or hide the comparison chart altogether, if you like.
83
+ * [Update] We've reintroduced the various levels of granularity for our charts: hourly (when a single day is selected), daily (for ranges up to 120 days) and monthly. Also, the comparison chart is now always available, regardless of the selected time range. Thank you, [WebsiteOpzetten](https://wordpress.org/support/topic/char-is-not-displayed-when-the-selected-time-range-is-comprised-of-a-single-day/).
84
+ * [Update] Tooltips across the interface have a more uniform behavior.
85
+
86
+ = 4.7.6.1 =
87
+ * [Fix] The new Javascript library was interfering with the dropdown menus on the WordPress Dashboard. Thanks to all of those who helped us troubleshoot the issue.
88
+
89
+ = 4.7.6 =
90
+ * [Note] As we mentioned earlier, we've been working on streamlining and cleaning up our source code. It's incredible how layers of code can deposit on top of each other, until they form a thick layer that prevents developers from seeing clearly what's happening. It was time to apply some virtual citric acid to descale our code. If you're using any kind of server caching functionality, please make sure to clear your cache before opening a support request. Also, do not hesitate to reach out to us if you notice any strange or unusual behaviors.
91
+ * [New] You can now download the geolocation map as a PDF file or PNG image. Thank you, Steve, for suggesting this feature!
92
+ * [New] You can now exclude pageviews by content type, like 404, taxonomy, search, etc. Thank you, [victor50g](https://wordpress.org/support/topic/suggestion-enable-filtering-on-content-404/).
93
+ * [Update] Deprecated the 'direction' filter, which was a leftover from over 5 years ago, when we used our old interface.
94
+ * [Fix] The cron job to archive old records was not firing as expected under certain circumstances.
95
+ * [Fix] Charts did not render correctly, if no data was available for that specific view.
96
+
97
+ = 4.7.5.3 =
98
+ * [New] For those who can't manage to automatically update their local copy of our premium add-ons, we've added a link to manually download the zip file. You'll find it under the Plugins screen, once you've entered and saved the corresponding license key under Slimstat > Add-ons.
99
+ * [Update] The [Update Checker](https://github.com/YahnisElsts/plugin-update-checker) library has been updated to version 4.4.
100
+ * [Fix] The world map was not being displayed correctly if no data points were available for the selected time range (thank you, Michel).
101
+
102
+ = 4.7.5.2 =
103
+ * [Update] You can now customize the amount of dots displayed on the World Map, under Slimstat > Settings > Reports > Access Log and World Map. Thank you, [service4](https://wordpress.org/support/topic/new-geolocation-map-with-cities/).
104
+ * [Fix] A dependency error was being highlighted for one of our premium add-ons under certain circumstances. Thank you, Peter.
105
+ * [Fix] The option to not set the session cookie was not working as expected. Thank you, [Bjarne](https://wordpress.org/support/topic/disable-cookies-2/#post-9887099).
106
+
107
+ = 4.7.5.1 =
108
+ * [Update] Implemented a workaround to try and fix the "Forbidden" error that a few users are experiencing when trying to download the MaxMind Geolite2 data file.
109
+ * [Fix] Updated the link to manually download the MaxMind data file from their servers, and added a new page to our knowledge base to explain how to manually install it.
110
+
111
+ = 4.7.5 =
112
+ * [New] Now that Slimstat is capable of geolocating visitors at the city level, wouldn't it make sense to display those visitors on the map? Well, of course! Go check out this new feature by accessing the Geolocation tab in Slimstat.
113
+ * [New] Updated the tracking script to handle events triggered by external libraries, like the [Vimeo API](https://github.com/vimeo/player.js/#events). Thank you, Max.
114
+ * [New] Added new operator "included_in_set", which allows you to list multiple values to match against, when composing a shortcode.
115
+ * [New] Added new option to avoid that Slimstat assigns a COOKIE to your visitors. Thank you, [dragon013](https://wordpress.org/support/topic/disable-cookies-2/).
116
+ * [Fix] A bug was preventing the feature to "restrict users" to only see their reports from working as expected.
117
+
118
+ = 4.7.4.1 =
119
+ * [Update] The Browscap data file is now loaded only when needed, thus removing its inherent overhead when unnecessary.
120
+ * [Update] The Browscap data file has been updated to the latest version available on their repository (ver 6026).
121
+ * [Fix] Addressed a remote XSS Vulnerability responsibly disclosed by one of our customers. Thank you, [riscybusiness](https://wordpress.org/support/topic/security-vulnerability-affecting-slimstat/).
122
+ * [Fix] Reintroduced the WHOIS pin, which has been removed by mistake because of a regression bug. Thank you, [brachialis](https://wordpress.org/support/topic/whois-location-icon-disappear/).
123
+
124
+ = 4.7.4 =
125
+ * [Update] New fields added to the Email Report and Export to Excel add-ons, by extending how certain reports are defined in core.
126
+ * [Fix] The [false positive](https://www.virustotal.com/#/file/43f69d9c4028f857b5b5544ea4559c03b4d58e02d75617482db517c626164363/detection) alert related to a virus in our code was fixed by updating [AmChart](https://www.amcharts.com/) to the latest version available (thank you, Sasa).
127
+ * [Fix] Removed a PHP warning of undefined index (thank you, [slewis1000 and Sasa](https://wordpress.org/support/topic/php-notice-undefined-index-country/))
128
+ * [Fix] The MozScape report was causing connectivity issues for some users, and it is now set as "hidden" by default.
129
+ * [Fix] Regression bug related to our Export to Excel add-on.
130
+
131
+ = 4.7.3.1 =
132
+ * [Fix] Apparently more people than we initially thought have issues with the MaxMind data file not being saved as expected. We are introducing a temporary fix while we try to investigate this issue further.
133
+
134
+ = 4.7.3 =
135
+ * [Fix] A [few users](https://wordpress.org/support/topic/cannot-install-maxmind-geolite-db/) pointed out a weird behavior when installing the MaxMind Geolocation data file, where an empty folder would be created instead of the actual file. If you still experience issues related to this problem, please make sure to delete the empty folder "maxmind.mmdb" under `wp-content/uploads/wp-slimstat/`.
136
+ * [Fix] Apparently Microsoft Security Essentials [was not pleased with our code](https://wordpress.org/support/topic/trojandownloader097m-donoff-detected-in-archive/), and was returning a false positive alert that a virus was included with the source code (thank you, Sasa).
137
+ * [Fix] The "content_id" filter could not be used in a shortcode to reference other pages (i.e. `[slimstat f='count-all' w='id']content_id equals 2012[/slimstat]`). Thank you, Felipe.
138
+ * [Fix] Country flags were not being displayed properly under certain circumstances (thank you, [Catmaniax](https://wordpress.org/support/topic/minor-issue-missing-png-file/)).
139
+ * [Fix] Bug preventing the new Heatmap Add-on from working as expected.
140
+
141
+ = 4.7.2.2 =
142
+ * [New] Added support for SCRIPT_DEBUG: by defining this constant in your `wp-config.php` will make Slimstat load the unminified version of the javascript tracker (thank you, Sasa)
143
+ * [Update] Added new parameter to make the `admin-ajax.php` URL relative, to solve issues like [this one](https://wordpress.org/support/topic/xmlhttprequest-cannot-load-wp-adminadmin-ajax-php-3/).
144
+ * [Fix] The Network Settings premium add-on was not working because of a bug in the main plugin. Thank you, Steve, for pointing us into the right direction.
145
+ * [Fix] Updated the schema (columns) for the archive table.
146
+
147
+
148
+ = 4.7.2.1 =
149
+ * [Fix] The new table columns "location" and "city" were not being created on a fresh install (thank you, [nielsgoossens](https://wordpress.org/support/topic/no-data-anymore-2/#post-9491034))
150
+ * [Fix] Async mode was not working as expected (thank you, [keithgbcc](https://wordpress.org/support/topic/doesnt-work-1694/#post-9487448))
151
+
152
+ = 4.7.2 =
153
+ * [New] As those who have been using Slimstat for a while know, we never stop doing our good share of research and development to improve our plugin. One feature on our wishlist was to make the geolocation functionality more accurate. Specifically, users have been asking us to track not just the Country of origin, but possibly the state and city. In order to geolocate visitors, our code has been leveraging a third-party data file provided by [MaxMind.com](https://www.maxmind.com/en/home). A while ago, they launched a new data format, which improves performance and offers a way to quickly determine the city of origin. However, the new library required a higher version of PHP, and up until now we had been hesitant to adopt it, to allow more people to use our plugin, over the chance of offering this feature. Now, after spending some time combing through their code, we found a way to get the best of both worlds: by customizing their PHP library, we were able to make it work with PHP 5.3! Which means that now Slimstat is able to tell you your visitors' city of origin (and State, when applicable) right out of the box. This information is available in the Access Log report and in a new 'Top Cities' report under the Audience tab. Please note: the MaxMind data file to enable this feature is approximately 60 Mb, and for this reason <strong>this new functionality is not enabled by default</strong>. You must go to Slimstat > Settings > Tracker and turn on the corresponding option. Then go to Slimstat > Settings > Maintenance and uninstall/install the GeoLite file to download the one that contains the city data. Please feel free to contact us if you have any questions.
154
+ * [Update] Removed backward compatibility code for those updating from a version prior to 4.2. Hopefully most of our users are using a newer version that that. If you're not, please contact our support service for instructions on how to upgrade.
155
+ * [Update] The format used to save your settings in the database has been changed. You MUST update your premium add-ons as soon as possible, and get the version compatible with this new format, or you might notice unexpected behaviors. Please contact us if you experience difficulties updating your add-ons.
156
+ * [Update] Cleaned up some old CSS code affecting the reports.
157
+
158
+ = 4.7.1 =
159
+ * [Fix] The new feature introduced in version 4.6.9.1 to allow our users to customize the default time range for the reports, had introduced a regression bug. Thank you to all our users who volunteered to test the bugfix.
160
+ * [Fix] A vulnerability has been disclosed by [Pluginvulnerabilities.com](pluginvulnerabilities.com): an attacker with admin credentials could leverage the import/export mechanism for the plugin's settings to inject some malicious code. We recommend that you upgrade to the latest version of Slimstat as soon as possible.
161
+ * [Fix] The new version of the [Add-on Update Checker library](https://github.com/YahnisElsts/plugin-update-checker), bundled with the previous release, was returning a fatal error under certain circumstances (thank you, Pepe).
162
+
wp-slimstat.php CHANGED
@@ -1,1966 +1,1967 @@
1
- <?php
2
- /*
3
- Plugin Name: Slimstat Analytics
4
- Plugin URI: http://wordpress.org/plugins/wp-slimstat/
5
- Description: The leading web analytics plugin for WordPress
6
- Version: 4.7.6.1
7
- Author: Jason Crouse
8
- Author URI: http://www.wp-slimstat.com/
9
- Text Domain: wp-slimstat
10
- Domain Path: /languages
11
- */
12
-
13
- if ( !empty( wp_slimstat::$settings ) ) {
14
- return true;
15
- }
16
-
17
- class wp_slimstat {
18
- public static $version = '4.7.6.1';
19
- public static $settings = array();
20
-
21
- public static $wpdb = '';
22
- public static $upload_dir = '';
23
- public static $maxmind_path = '';
24
-
25
- public static $update_checker = array();
26
- public static $raw_post_array = array();
27
-
28
- protected static $data_js = array( 'id' => 0 );
29
- protected static $stat = array();
30
- protected static $settings_signature = '';
31
- protected static $browser = array();
32
- protected static $heuristic_key = 0;
33
- protected static $date_i18n_filters = array();
34
-
35
- /**
36
- * Initializes variables and actions
37
- */
38
- public static function init() {
39
-
40
- // Load all the settings
41
- if ( is_network_admin() && ( empty($_GET[ 'page' ] ) || strpos( $_GET[ 'page' ], 'slimview' ) === false ) ) {
42
- self::$settings = get_site_option( 'slimstat_options', array() );
43
- }
44
- else {
45
- self::$settings = get_option( 'slimstat_options', array() );
46
- }
47
-
48
- self::$settings = array_merge( self::init_options(), self::$settings );
49
-
50
- // Allow third party tools to edit the options
51
- self::$settings = apply_filters( 'slimstat_init_options', self::$settings );
52
-
53
- // Determine the options' signature: if it hasn't changed, there's no need to update/save them in the database
54
- self::$settings_signature = md5( serialize( self::$settings ) );
55
-
56
- // Allow third-party tools to use a custom database for Slimstat
57
- self::$wpdb = apply_filters( 'slimstat_custom_wpdb', $GLOBALS[ 'wpdb' ] );
58
-
59
- // Hook a DB clean-up routine to the daily cronjob
60
- add_action( 'wp_slimstat_purge', array( __CLASS__, 'wp_slimstat_purge' ) );
61
-
62
- // Allow external domains on CORS requests
63
- add_filter( 'allowed_http_origins', array(__CLASS__, 'open_cors_admin_ajax' ) );
64
-
65
- // Define the folder where to store the geolocation database (shared among sites in a network, by default)
66
- self::$upload_dir = wp_upload_dir();
67
- self::$upload_dir = self::$upload_dir[ 'basedir' ];
68
- if ( is_multisite() && ! ( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
69
- self::$upload_dir = str_replace( '/sites/' . get_current_blog_id(), '', self::$upload_dir );
70
- }
71
- self::$upload_dir .= '/wp-slimstat';
72
- self::$upload_dir = apply_filters( 'slimstat_maxmind_path', self::$upload_dir );
73
-
74
- self::$maxmind_path = self::$upload_dir . '/maxmind.mmdb';
75
-
76
- // Enable the tracker (both server- and client-side)
77
- if ( !is_admin() || self::$settings[ 'track_admin_pages' ] == 'on' ) {
78
-
79
- // Allow add-ons to turn off the tracker based on other conditions
80
- $is_tracking_filter = apply_filters( 'slimstat_filter_pre_tracking', strpos( self::get_request_uri(), 'wp-admin/admin-ajax.php' ) === false );
81
- $is_tracking_filter_js = apply_filters( 'slimstat_filter_pre_tracking_js', true );
82
-
83
- // Is server-side tracking active?
84
- if ( self::$settings[ 'javascript_mode' ] != 'on' && self::$settings[ 'is_tracking' ] == 'on' && $is_tracking_filter ) {
85
- add_action( is_admin() ? 'admin_init' : 'wp', array( __CLASS__, 'slimtrack' ), 5 );
86
-
87
- if ( self::$settings[ 'track_users' ] == 'on' ) {
88
- add_action( 'login_init', array( __CLASS__, 'slimtrack' ), 10 );
89
- }
90
- }
91
-
92
- // Slimstat tracks screen resolutions, outbound links and other client-side information using a client-side tracker
93
- add_action( is_admin() ? 'admin_enqueue_scripts' : 'wp_enqueue_scripts' , array( __CLASS__, 'wp_slimstat_enqueue_tracking_script' ), 15 );
94
- if ( self::$settings[ 'track_users' ] == 'on' ) {
95
- add_action( 'login_enqueue_scripts', array( __CLASS__, 'wp_slimstat_enqueue_tracking_script' ), 10 );
96
- }
97
- }
98
-
99
- // Shortcodes
100
- add_shortcode('slimstat', array(__CLASS__, 'slimstat_shortcode'), 15);
101
-
102
- // Load the admin library
103
- if ( is_user_logged_in() ) {
104
- include_once ( plugin_dir_path( __FILE__ ) . 'admin/wp-slimstat-admin.php' );
105
- add_action( 'init', array( 'wp_slimstat_admin', 'init' ), 60 );
106
- }
107
-
108
- // Include our browser detector library
109
- include_once( plugin_dir_path( __FILE__ ) . 'browscap/browser.php' );
110
- add_action( 'init', array( 'slim_browser', 'init' ) );
111
-
112
- // If add-ons are installed, check for updates
113
- add_action( 'wp_loaded', array( __CLASS__, 'update_checker' ) );
114
-
115
- // Update the options before shutting down
116
- add_action( 'shutdown', array( __CLASS__, 'slimstat_save_options' ), 100 );
117
-
118
- // REST API Support
119
- add_action( 'rest_api_init', array( __CLASS__, 'register_rest_route' ) );
120
- }
121
- // end init
122
-
123
- /**
124
- * Ajax Tracking
125
- */
126
- public static function slimtrack_ajax() {
127
- // If the website is using a caching plugin, the tracking code might still be there, even if the user turned off tracking
128
- if ( self::$settings[ 'is_tracking' ] != 'on' ) {
129
- self::$stat[ 'id' ] = -204;
130
- self::_set_error_array( __( 'Tracker is turned off, but client-side tracking code is still running.', 'wp-slimstat' ), true );
131
- self::slimstat_save_options();
132
- exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
133
- }
134
-
135
- // This function also initializes self::$data_js and removes the checksum from self::$data_js['id']
136
- self::_check_data_integrity( self::$raw_post_array );
137
-
138
- // Is this a request to record a new pageview?
139
- if ( self::$data_js[ 'op' ] == 'add' || self::$data_js[ 'op' ] == 'update' ) {
140
-
141
- // Track client-side information (screen resolution, plugins, etc)
142
- if ( !empty( self::$data_js[ 'bw' ] ) ) {
143
- self::$stat[ 'resolution' ] = strip_tags( trim( self::$data_js[ 'bw' ] . 'x' . self::$data_js[ 'bh' ] ) );
144
- }
145
- if ( !empty( self::$data_js[ 'sw' ] ) ) {
146
- self::$stat[ 'screen_width' ] = intval( self::$data_js[ 'sw' ] );
147
- }
148
- if ( !empty( self::$data_js[ 'sh' ] ) ) {
149
- self::$stat[ 'screen_height' ] = intval( self::$data_js[ 'sh' ] );
150
- }
151
- if ( !empty( self::$data_js[ 'pl' ] ) ) {
152
- self::$stat[ 'plugins' ] = strip_tags( trim( self::$data_js[ 'pl' ] ) );
153
- }
154
- if ( !empty( self::$data_js[ 'sl' ] ) && self::$data_js[ 'sl' ] > 0 && self::$data_js[ 'sl' ] < 60000 ) {
155
- self::$stat[ 'server_latency' ] = intval( self::$data_js[ 'sl' ] );
156
- }
157
- if ( !empty( self::$data_js[ 'pp' ] ) && self::$data_js[ 'pp' ] > 0 && self::$data_js[ 'pp' ] < 60000 ) {
158
- self::$stat[ 'page_performance' ] = intval( self::$data_js[ 'pp' ] );
159
- }
160
- }
161
-
162
- if ( self::$data_js[ 'op' ] == 'add' ) {
163
- self::slimtrack();
164
- }
165
- else if ( self::$data_js[ 'op' ] == 'update' ) {
166
- // Update an existing pageview with client-based information (resolution, plugins installed, etc)
167
- self::_set_visit_id( true );
168
-
169
- // ID of the pageview to update
170
- self::$stat[ 'id' ] = abs( intval( self::$data_js[ 'id' ] ) );
171
-
172
- // Visitor is still on this page, record the timestamp in the corresponding field
173
- self::toggle_date_i18n_filters( false );
174
- self::$stat['dt_out'] = date_i18n( 'U' );
175
- self::toggle_date_i18n_filters( true );
176
-
177
- // Are we tracking an outbound click?
178
- if ( !empty( self::$data_js[ 'res' ] ) ) {
179
- $outbound_resource = strip_tags( trim( base64_decode( self::$data_js[ 'res' ] ) ) );
180
- $outbound_host = parse_url( $outbound_resource, PHP_URL_HOST );
181
- $site_host = parse_url( get_site_url(), PHP_URL_HOST );
182
- if ( $outbound_host != $site_host ) {
183
- $existing_outbound_resource = self::$wpdb->get_var( self::$wpdb->prepare( "
184
- SELECT outbound_resource
185
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
186
- WHERE id = %d", self::$stat[ 'id' ]
187
- ) );
188
-
189
- self::$stat[ 'outbound_resource' ] = $outbound_resource;
190
- if ( !empty( $existing_outbound_resource ) ) {
191
- self::$stat[ 'outbound_resource' ] = $existing_outbound_resource . ';;;' .$outbound_resource;
192
- }
193
- }
194
- }
195
-
196
- self::update_row( self::$stat, $GLOBALS[ 'wpdb' ]->prefix . 'slim_stats' );
197
- }
198
-
199
- // Are we tracking events, coordinates and other details?
200
- if ( self::$data_js[ 'op' ] == 'event' || !empty( self::$data_js[ 'pos' ] ) ) {
201
- self::$stat[ 'id' ] = abs( intval( self::$data_js[ 'id' ] ) );
202
-
203
- self::toggle_date_i18n_filters( false );
204
- $event_info = array(
205
- 'position' => strip_tags( trim( self::$data_js[ 'pos' ] ) ),
206
- 'id' => self::$stat[ 'id' ],
207
- 'dt' => date_i18n( 'U' )
208
- );
209
- self::toggle_date_i18n_filters( true );
210
-
211
- if ( !empty( self::$data_js[ 'ty' ] ) ) {
212
- $event_info[ 'type' ] = abs( intval( self::$data_js[ 'ty' ] ) );
213
- }
214
- if ( !empty( self::$data_js[ 'des' ] ) ) {
215
- $event_info[ 'event_description' ] = strip_tags( trim( base64_decode( self::$data_js[ 'des' ] ) ) );
216
- }
217
- if ( !empty( self::$data_js[ 'no' ] ) ) {
218
- $event_info[ 'notes' ] = strip_tags( trim( base64_decode( self::$data_js[ 'no' ] ) ) );
219
- }
220
-
221
- self::insert_row( $event_info, $GLOBALS[ 'wpdb' ]->prefix . 'slim_events' );
222
- }
223
-
224
- // Was this pageview tracked?
225
- if ( self::$stat[ 'id' ] <= 0 ) {
226
- $abs_error_code = abs( self::$stat[ 'id' ] );
227
- do_action( 'slimstat_track_exit_' . $abs_error_code, self::$stat );
228
- self::slimstat_save_options();
229
- exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
230
- }
231
-
232
- // Send the ID back to Javascript to track future interactions
233
- do_action( 'slimstat_track_success' );
234
-
235
- // If we tracked an internal download, we return the original ID, not the new one
236
- exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
237
- }
238
-
239
- /**
240
- * Core tracking functionality
241
- */
242
- public static function slimtrack( $_argument = '' ) {
243
- // If the website is using a caching plugin, the tracking code might still be there, even if the user turned off tracking
244
- if ( self::$settings[ 'is_tracking' ] != 'on' ) {
245
- self::$stat[ 'id' ] = -204;
246
- self::_set_error_array( __( 'Tracker is turned off, but client-side tracking code is still running.', 'wp-slimstat' ), true );
247
- return $_argument;
248
- }
249
-
250
- self::toggle_date_i18n_filters( false );
251
- self::$stat[ 'dt' ] = date_i18n( 'U' );
252
- self::$stat[ 'notes' ] = array();
253
- self::toggle_date_i18n_filters( true );
254
-
255
- // Allow third-party tools to initialize the stat array
256
- self::$stat = apply_filters( 'slimstat_filter_pageview_stat_init', self::$stat );
257
-
258
- // Third-party tools can decide that this pageview should not be tracked, by setting its datestamp to zero
259
- if ( empty( self::$stat ) || empty( self::$stat[ 'dt' ] ) ) {
260
- self::$stat[ 'id' ] = -300;
261
- self::_set_error_array( __( 'Pageview filtered by third-party code', 'wp-slimstat' ), true );
262
- return $_argument;
263
- }
264
-
265
- // User's IP address
266
- list ( self::$stat[ 'ip' ], self::$stat[ 'other_ip' ] ) = self::_get_remote_ip();
267
-
268
- if ( empty( self::$stat[ 'ip' ] ) || self::$stat[ 'ip' ] == '0.0.0.0' ) {
269
- self::$stat[ 'id' ] = -202;
270
- self::_set_error_array( __( 'Empty or not supported IP address format (IPv6)', 'wp-slimstat' ), false );
271
- return $_argument;
272
- }
273
-
274
- // Referrer URL
275
- if ( !empty( self::$data_js[ 'ref' ] ) ) {
276
- self::$stat[ 'referer' ] = base64_decode( self::$data_js[ 'ref' ] );
277
- }
278
- else if ( !empty( $_SERVER[ 'HTTP_REFERER' ] ) ) {
279
- self::$stat[ 'referer' ] = $_SERVER[ 'HTTP_REFERER' ];
280
- }
281
-
282
- if ( !empty( self::$stat[ 'referer' ] ) ) {
283
-
284
- // Is this a 'seriously malformed' URL?
285
- $referer = parse_url( self::$stat[ 'referer' ] );
286
- if ( !$referer ) {
287
- self::_set_error_array( sprintf( __( 'Malformed referrer URL: %s (IP: %s)', 'wp-slimstat' ), self::$stat[ 'referer' ], self::$stat[ 'ip' ] ), false, 201 );
288
- self::$stat[ 'notes' ][] = sprintf( __( 'Malformed referrer URL: %s', 'wp-slimstat' ), self::$stat[ 'referer' ] );
289
- unset( self::$stat[ 'referer' ] );
290
- }
291
-
292
- if ( !empty( $referer[ 'scheme' ] ) && !in_array( strtolower( $referer[ 'scheme' ] ), array( 'http', 'https', 'android-app' ) ) ) {
293
- self::_set_error_array( sprintf( __( 'Attempted XSS Injection: %s (IP: %s)', 'wp-slimstat' ), self::$stat[ 'referer' ], self::$stat[ 'ip' ] ), false, 203 );
294
- self::$stat[ 'notes' ][] = sprintf( __( 'Attempted XSS Injection: %s', 'wp-slimstat' ), self::$stat[ 'referer' ] );
295
- unset( self::$stat[ 'referer' ] );
296
- }
297
-
298
- if ( !empty( self::$stat[ 'referer' ] ) ) {
299
- $parsed_site_url_host = parse_url( get_site_url(), PHP_URL_HOST );
300
- if ( !empty( $referer[ 'host' ] ) && $referer[ 'host' ] == $parsed_site_url_host && self::$settings[ 'track_same_domain_referers' ] != 'on' ) {
301
- unset( self::$stat[ 'referer' ] );
302
- }
303
- else {
304
- // Fix Google Images referring domain
305
- if ( strpos( self::$stat[ 'referer' ], 'www.google' ) !== false ) {
306
- if ( strpos( self::$stat[ 'referer' ], '/imgres?' ) !== false ) {
307
- self::$stat[ 'referer' ] = str_replace( 'www.google', 'images.google', self::$stat[ 'referer' ] );
308
- }
309
- if ( strpos( self::$stat[ 'referer' ], '/url?' ) !== false ) {
310
- self::$stat[ 'referer' ] = str_replace( '/url?', '/search?', self::$stat[ 'referer' ] );
311
- }
312
- }
313
-
314
- // Is this referer blacklisted?
315
- if ( !empty( self::$settings[ 'ignore_referers' ] ) ) {
316
- $return_error_code = array(
317
- -301,
318
- sprintf( __( 'Referrer %s is blacklisted', 'wp-slimstat' ), self::$stat[ 'referer' ] ),
319
- true
320
- );
321
- if ( self::_is_blacklisted( self::$stat[ 'referer' ], self::$settings[ 'ignore_referers' ], $return_error_code ) ) {
322
- return $_argument;
323
- }
324
- }
325
- }
326
- }
327
- }
328
-
329
- $content_info = self::_get_content_info();
330
-
331
- // Is this content type blacklisted?
332
- if ( !empty( self::$settings[ 'ignore_content_types' ] ) ) {
333
- $return_error_code = array(
334
- -313,
335
- sprintf( __( 'Content Type %s is blacklisted', 'wp-slimstat' ), $content_info[ 'content_type' ] ),
336
- true
337
- );
338
- if ( self::_is_blacklisted( $content_info[ 'content_type' ], self::$settings[ 'ignore_content_types' ], $return_error_code ) ) {
339
- return $_argument;
340
- }
341
- }
342
-
343
- // Did we receive data from an Ajax request?
344
- if ( !empty( self::$data_js[ 'id' ] ) ) {
345
-
346
- // Are we tracking a new pageview? (pos is empty = no event was triggered)
347
- if ( empty( self::$data_js[ 'pos' ] ) ) {
348
- $content_info = unserialize( base64_decode( self::$data_js[ 'id' ] ) );
349
- if ( $content_info === false || empty( $content_info[ 'content_type' ] ) ) {
350
- $content_info = array();
351
- }
352
- }
353
-
354
- // If pos is not empty and slimtrack was called, it means we are tracking a new internal download
355
- else if ( !empty( self::$data_js[ 'res' ] ) ) {
356
- $download_url = base64_decode( self::$data_js[ 'res' ] );
357
- if ( is_string( $download_url ) ) {
358
- if ( !empty( self::$data_js[ 'ty' ] ) && self::$data_js[ 'ty' ] == 1 ) {
359
- unset( self::$stat[ 'id' ] );
360
- $content_info = array( 'content_type' => 'download' );
361
- }
362
- }
363
- }
364
- }
365
-
366
- self::$stat = self::$stat + $content_info;
367
-
368
- // We want to record both hits and searches (performed through the site search form)
369
- if ( is_array( self::$data_js ) && isset( self::$data_js[ 'res' ] ) ) {
370
- $decoded_permalink = base64_decode( self::$data_js[ 'res' ] );
371
- $parsed_permalink = parse_url( $decoded_permalink );
372
- if ( !empty( $referer ) ) {
373
- self::$stat[ 'searchterms' ] = self::_get_search_terms( $referer );
374
- }
375
-
376
- // Was this an internal search?
377
- if ( empty( self::$stat[ 'searchterms' ] ) ) {
378
- self::$stat[ 'searchterms' ] = self::_get_search_terms( $parsed_permalink );
379
- }
380
-
381
- if ( self::$stat[ 'content_type' ] == 'external' ) {
382
- self::$stat['resource'] = $decoded_permalink;
383
- }
384
- else {
385
- self::$stat['resource'] = !is_array( $parsed_permalink ) ? __( 'Malformed URL', 'wp-slimstat' ) : urldecode( $parsed_permalink[ 'path' ] ) . ( !empty( $parsed_permalink[ 'query' ] ) ? '?' . urldecode( $parsed_permalink[ 'query' ] ) : '' );
386
- }
387
- }
388
- elseif ( empty( $_REQUEST[ 's' ] ) ) {
389
- if ( !empty( $referer ) ) {
390
- self::$stat[ 'searchterms' ] = self::_get_search_terms( $referer );
391
- }
392
- self::$stat[ 'resource' ] = self::get_request_uri();
393
- }
394
- else {
395
- self::$stat[ 'resource' ] = parse_url( self::get_request_uri(), PHP_URL_PATH );
396
- self::$stat[ 'searchterms' ] = str_replace( '\\', '', $_REQUEST[ 's' ] );
397
- self::$stat[ 'referer' ] = self::get_request_uri();
398
- if ( isset( $GLOBALS['wp_query']->found_posts ) ) {
399
- self::$stat[ 'notes' ][] = 'results:' . intval( $GLOBALS['wp_query']->found_posts );
400
- }
401
- }
402
-
403
- // Don't store empty values in the database
404
- if ( empty( self::$stat[ 'searchterms' ] ) ) {
405
- unset( self::$stat[ 'searchterms' ] );
406
- }
407
-
408
- // Do not track report pages in the admin
409
- if ( ( !empty( self::$stat[ 'resource' ] ) && strpos( self::$stat[ 'resource' ], 'wp-admin/admin-ajax.php' ) !== false ) || ( !empty( $_GET[ 'page' ] ) && strpos( $_GET[ 'page' ], 'slimview' ) !== false ) ) {
410
- self::$stat = array();
411
- return $_argument;
412
- }
413
-
414
- // Is this resource blacklisted?
415
- if ( !empty( self::$settings[ 'ignore_resources' ] ) ) {
416
- $return_error_code = array(
417
- -302,
418
- sprintf( __( 'Permalink %s is blacklisted', 'wp-slimstat' ), self::$stat[ 'resource' ] ),
419
- true
420
- );
421
- if ( self::_is_blacklisted( self::$stat[ 'resource' ], self::$settings[ 'ignore_resources' ], $return_error_code ) ) {
422
- return $_argument;
423
- }
424
- }
425
-
426
- // Should we ignore this user?
427
- if ( !empty( $GLOBALS[ 'current_user' ]->ID ) ) {
428
- // Don't track logged-in users, if the corresponding option is enabled
429
- if ( self::$settings[ 'track_users' ] == 'no' ) {
430
- self::$stat[ 'id' ] = -303;
431
- self::_set_error_array( sprintf( __( 'Logged in user %s not tracked', 'wp-slimstat' ), $GLOBALS[ 'current_user' ]->data->user_login ), true );
432
- return $_argument;
433
- }
434
-
435
- // Don't track users with given capabilities
436
- foreach ( self::string_to_array( self::$settings[ 'ignore_capabilities' ] ) as $a_capability ) {
437
- if ( array_key_exists( strtolower( $a_capability ), $GLOBALS[ 'current_user' ]->allcaps ) ) {
438
- self::$stat[ 'id' ] = -304;
439
- self::_set_error_array( sprintf( __( 'User with capability %s not tracked', 'wp-slimstat' ), $a_capability ), true );
440
- return $_argument;
441
- }
442
- }
443
-
444
- // Is this user blacklisted?
445
- if ( !empty( self::$settings[ 'ignore_users' ] ) ) {
446
- $return_error_code = array(
447
- -305,
448
- sprintf( __( 'User %s is blacklisted', 'wp-slimstat' ), $GLOBALS[ 'current_user' ]->data->user_login ),
449
- true
450
- );
451
- if ( self::_is_blacklisted( $GLOBALS[ 'current_user' ]->data->user_login, self::$settings[ 'ignore_users' ], $return_error_code ) ) {
452
- return $_argument;
453
- }
454
- }
455
-
456
- self::$stat[ 'username' ] = $GLOBALS[ 'current_user' ]->data->user_login;
457
- self::$stat[ 'notes' ][] = 'user:' . $GLOBALS[ 'current_user' ]->data->ID;
458
- $not_spam = true;
459
- }
460
- elseif ( isset( $_COOKIE[ 'comment_author_' . COOKIEHASH ] ) ) {
461
- // Is this a spammer?
462
- $spam_comment = self::$wpdb->get_row( self::$wpdb->prepare( "
463
- SELECT comment_author, COUNT(*) comment_count
464
- FROM `" . DB_NAME . "`.{$GLOBALS['wpdb']->comments}
465
- WHERE comment_author_IP = %s AND comment_approved = 'spam'
466
- GROUP BY comment_author
467
- LIMIT 0,1", self::$stat[ 'ip' ] ), ARRAY_A );
468
-
469
- if ( !empty( $spam_comment[ 'comment_count' ] ) ) {
470
- if ( self::$settings[ 'ignore_spammers' ] == 'on' ){
471
- self::$stat[ 'id' ] = -306;
472
- self::_set_error_array( sprintf( __( 'Spammer %s not tracked', 'wp-slimstat' ), $spam_comment[ 'comment_author' ] ), true );
473
- return $_argument;
474
- }
475
- else{
476
- self::$stat[ 'notes' ][] = 'spam:yes';
477
- self::$stat[ 'username' ] = $spam_comment[ 'comment_author' ];
478
- }
479
- }
480
- else
481
- self::$stat[ 'username' ] = $_COOKIE[ 'comment_author_' . COOKIEHASH ];
482
- }
483
-
484
- // Should we ignore this IP address?
485
- foreach ( self::string_to_array( self::$settings[ 'ignore_ip' ] ) as $a_ip_range ) {
486
- $ip_to_ignore = $a_ip_range;
487
-
488
- if ( strpos( $ip_to_ignore, '/' ) !== false ) {
489
- list( $ip_to_ignore, $cidr_mask ) = explode( '/', trim( $ip_to_ignore ) );
490
- }
491
- else{
492
- $cidr_mask = self::get_mask_length( $ip_to_ignore );
493
- }
494
-
495
- $long_masked_ip_to_ignore = substr( self::dtr_pton( $ip_to_ignore ), 0, $cidr_mask );
496
- $long_masked_user_ip = substr( self::dtr_pton( self::$stat[ 'ip' ] ), 0, $cidr_mask );
497
- $long_masked_user_other_ip = substr( self::dtr_pton( self::$stat[ 'other_ip' ] ), 0 , $cidr_mask );
498
-
499
- if ( $long_masked_user_ip === $long_masked_ip_to_ignore || $long_masked_user_other_ip === $long_masked_ip_to_ignore ) {
500
- self::$stat[ 'id' ] = -307;
501
- self::_set_error_array( sprintf( __( 'IP address %s is blacklisted', 'wp-slimstat' ), self::$stat[ 'ip' ] . ( !empty( self::$stat[ 'other_ip' ] ) ? ' (' . self::$stat[ 'other_ip' ] . ')' : '' ) ), true );
502
- return $_argument;
503
- }
504
- }
505
-
506
- // Language
507
- self::$stat[ 'language' ] = self::_get_language();
508
-
509
- // Geolocation
510
- include_once ( plugin_dir_path( __FILE__ ) . 'maxmind.php' );
511
- $geolocation_data = maxmind_geolite2_connector::get_geolocation_info( self::$stat[ 'ip' ] );
512
-
513
- // Invalid MaxMind data file
514
-
515
- if ( !empty( $geolocation_data[ 'country' ][ 'iso_code' ] ) ) {
516
-
517
- if ( $geolocation_data[ 'country' ][ 'iso_code' ] == 'xx' ) {
518
- self::_set_error_array( __( 'Your MaxMind data file is invalid. Please uninstall it using the button in Settings > Maintenance.', 'wp-slimstat' ), false, 205 );
519
- }
520
- else {
521
- self::$stat[ 'country' ] = strtolower( $geolocation_data[ 'country' ][ 'iso_code' ] );
522
-
523
-
524
- if ( !empty( $geolocation_data[ 'city' ][ 'names' ][ 'en' ] ) ) {
525
- self::$stat[ 'city' ] = $geolocation_data[ 'city' ][ 'names' ][ 'en' ];
526
- }
527
-
528
- if ( !empty( $geolocation_data[ 'subdivisions' ][ 0 ][ 'iso_code' ] ) && !empty( self::$stat[ 'city' ] ) ) {
529
- self::$stat[ 'city' ] .= ' (' . $geolocation_data[ 'subdivisions' ][ 0 ][ 'iso_code' ] . ')';
530
- }
531
-
532
- if ( !empty( $geolocation_data[ 'location' ][ 'latitude' ] ) && !empty( $geolocation_data[ 'location' ][ 'longitude' ] ) ) {
533
- self::$stat[ 'location' ] = $geolocation_data[ 'location' ][ 'latitude' ] . ',' . $geolocation_data[ 'location' ][ 'longitude' ];
534
- }
535
- }
536
- }
537
-
538
- unset( $geolocation_data );
539
-
540
- // Anonymize IP Address?
541
- if ( self::$settings[ 'anonymize_ip' ] == 'on' ) {
542
- // IPv4 or IPv6
543
- $needle = '.';
544
- $replace = '.0';
545
- if ( self::get_mask_length( self::$stat['ip'] ) == 128 ) {
546
- $needle = ':';
547
- $replace = ':0000';
548
- }
549
-
550
- self::$stat[ 'ip' ] = substr( self::$stat[ 'ip' ], 0, strrpos( self::$stat[ 'ip' ], $needle ) ) . $replace;
551
-
552
- if ( !empty( self::$stat[ 'other_ip' ] ) ) {
553
- self::$stat[ 'other_ip' ] = substr( self::$stat[ 'other_ip' ], 0, strrpos( self::$stat[ 'other_ip' ], $needle ) ) . $replace;
554
- }
555
- }
556
-
557
- // Is this country blacklisted?
558
- if ( !empty( self::$stat[ 'country' ] ) && !empty( self::$settings[ 'ignore_countries' ] ) && stripos( self::$settings[ 'ignore_countries' ], self::$stat[ 'country' ] ) !== false ) {
559
- self::$stat['id'] = -308;
560
- self::_set_error_array( sprintf( __('Country %s is blacklisted', 'wp-slimstat'), self::$stat[ 'country' ] ), true );
561
- return $_argument;
562
- }
563
-
564
- // Mark or ignore Firefox/Safari prefetching requests (X-Moz: Prefetch and X-purpose: Preview)
565
- if ( ( isset( $_SERVER[ 'HTTP_X_MOZ' ] ) && ( strtolower( $_SERVER[ 'HTTP_X_MOZ' ] ) == 'prefetch' ) ) ||
566
- ( isset( $_SERVER[ 'HTTP_X_PURPOSE' ] ) && ( strtolower( $_SERVER[ 'HTTP_X_PURPOSE' ] ) == 'preview' ) ) ) {
567
- if ( self::$settings[ 'ignore_prefetch' ] == 'on' ) {
568
- self::$stat[ 'id' ] = -309;
569
- self::_set_error_array( __( 'Prefetch requests are ignored', 'wp-slimstat' ), true );
570
- return $_argument;
571
- }
572
- else{
573
- self::$stat[ 'notes' ][] = 'pre:yes';
574
- }
575
- }
576
-
577
- // Detect user agent
578
- if ( empty( self::$browser ) ) {
579
- self::$browser = slim_browser::get_browser();
580
- }
581
-
582
- // Are we ignoring bots?
583
- if ( ( self::$settings[ 'javascript_mode' ] == 'on' || self::$settings[ 'ignore_bots' ] == 'on' ) && self::$browser[ 'browser_type' ] == 1 ) {
584
- self::$stat[ 'id' ] = -310;
585
- self::_set_error_array( __( 'Bot not tracked', 'wp-slimstat' ), true );
586
- return $_argument;
587
- }
588
-
589
- // Is this browser blacklisted?
590
- if ( !empty( self::$settings[ 'ignore_browsers' ] ) ) {
591
- $return_error_code = array(
592
- -311,
593
- sprintf( __( 'Browser %s is blacklisted', 'wp-slimstat' ), self::$browser[ 'browser' ] ),
594
- true
595
- );
596
- if ( self::_is_blacklisted( array( self::$browser[ 'browser' ], self::$browser[ 'user_agent' ] ), self::$settings[ 'ignore_browsers' ], $return_error_code ) ) {
597
- return $_argument;
598
- }
599
- }
600
-
601
- // Is this operating system blacklisted?
602
- if ( !empty( self::$settings[ 'ignore_platforms' ] ) ) {
603
- $return_error_code = array(
604
- -312,
605
- sprintf( __( 'Operating System %s is blacklisted', 'wp-slimstat' ), self::$browser[ 'platform' ] ),
606
- true
607
- );
608
- if ( self::_is_blacklisted( self::$browser[ 'platform' ], self::$settings[ 'ignore_platforms' ], $return_error_code ) ) {
609
- return $_argument;
610
- }
611
- }
612
-
613
- self::$stat = self::$stat + self::$browser;
614
-
615
- // This function can be called in "simulate" mode: no data will be actually saved in the database
616
- if ( is_array( $_argument ) && isset( $_argument[ 'slimtrack_simulate' ] ) ) {
617
- $_argument[ 'slimtrack_would_track' ] = true;
618
- return ( $_argument );
619
- }
620
-
621
- // Do we need to assign a visit_id to this user?
622
- $cookie_has_been_set = self::_set_visit_id( false );
623
-
624
- // Allow third-party tools to modify all the data we've gathered so far
625
- self::$stat = apply_filters( 'slimstat_filter_pageview_stat', self::$stat );
626
- do_action( 'slimstat_track_pageview', self::$stat );
627
-
628
- // Third-party tools can decide that this pageview should not be tracked, by setting its datestamp to zero
629
- if (empty(self::$stat) || empty(self::$stat['dt'])){
630
- self::$stat['id'] = -300;
631
- self::_set_error_array( __( 'Pageview filtered by third-party code', 'wp-slimstat' ), true );
632
- return $_argument;
633
- }
634
-
635
- // Implode the notes
636
- if ( !empty( self::$stat[ 'notes' ] ) ) {
637
- self::$stat[ 'notes' ] = implode( ';', self::$stat[ 'notes' ] );
638
- }
639
- else {
640
- unset( self::$stat[ 'notes' ] );
641
- }
642
-
643
- // Now let's save this information in the database
644
- self::$stat[ 'id' ] = self::insert_row( self::$stat, $GLOBALS[ 'wpdb' ]->prefix . 'slim_stats' );
645
-
646
- // Something went wrong during the insert
647
- if ( empty( self::$stat[ 'id' ] ) ) {
648
-
649
- // Attempt to init the environment (plugin just activated on a blog in a MU network?)
650
- include_once ( plugin_dir_path( __FILE__ ) . 'admin/wp-slimstat-admin.php' );
651
- wp_slimstat_admin::init_environment( true );
652
-
653
- // Now let's try again
654
- self::$stat['id'] = self::insert_row(self::$stat, $GLOBALS['wpdb']->prefix.'slim_stats');
655
-
656
- if ( empty( self::$stat[ 'id' ] ) ) {
657
- self::$stat[ 'id' ] = -200;
658
- self::_set_error_array( self::$wpdb->last_error, false );
659
- return $_argument;
660
- }
661
- }
662
-
663
- // Is this a new visitor?
664
- $set_cookie = apply_filters( 'slimstat_set_visit_cookie', ( !empty( self::$settings[ 'set_tracker_cookie' ] ) && self::$settings[ 'set_tracker_cookie' ] == 'on' ) );
665
- if ( $set_cookie ) {
666
- if ( empty( self::$stat[ 'visit_id' ] ) && !empty( self::$stat[ 'id' ] ) ) {
667
- // Set a cookie to track this visit (Google and other non-human engines will just ignore it)
668
- @setcookie(
669
- 'slimstat_tracking_code',
670
- self::_get_id_with_checksum( self::$stat[ 'id' ] . 'id' ),
671
- time() + 2678400, // one month
672
- COOKIEPATH
673
- );
674
- }
675
- elseif ( !$cookie_has_been_set && self::$settings[ 'extend_session' ] == 'on' && self::$stat[ 'visit_id' ] > 0 ) {
676
- @setcookie(
677
- 'slimstat_tracking_code',
678
- self::_get_id_with_checksum( self::$stat[ 'visit_id' ] ),
679
- time() + self::$settings[ 'session_duration' ],
680
- COOKIEPATH
681
- );
682
- }
683
- }
684
-
685
- return $_argument;
686
- }
687
- // end slimtrack
688
-
689
- /**
690
- * Decodes the permalink
691
- */
692
- public static function get_request_uri(){
693
- if (isset($_SERVER['REQUEST_URI'])){
694
- return urldecode($_SERVER['REQUEST_URI']);
695
- }
696
- elseif (isset($_SERVER['SCRIPT_NAME'])){
697
- return isset($_SERVER['QUERY_STRING'])?$_SERVER['SCRIPT_NAME']."?".$_SERVER['QUERY_STRING']:$_SERVER['SCRIPT_NAME'];
698
- }
699
- else{
700
- return isset($_SERVER['QUERY_STRING'])?$_SERVER['PHP_SELF']."?".$_SERVER['QUERY_STRING']:$_SERVER['PHP_SELF'];
701
- }
702
- }
703
- // end get_request_uri
704
-
705
- /**
706
- * Stores the information (array) in the appropriate table and returns the corresponding ID
707
- */
708
- public static function insert_row($_data = array(), $_table = ''){
709
- if ( empty( $_data ) || empty( $_table ) ) {
710
- return -1;
711
- }
712
-
713
- // Remove unwanted characters (SQL injections, anyone?)
714
- $data_keys = array();
715
- foreach (array_keys($_data) as $a_key){
716
- $data_keys[] = sanitize_key($a_key);
717
- }
718
-
719
- self::$wpdb->query(self::$wpdb->prepare("
720
- INSERT IGNORE INTO $_table (".implode(", ", $data_keys).')
721
- VALUES ('.substr(str_repeat('%s,', count($_data)), 0, -1).")", $_data));
722
-
723
- return intval(self::$wpdb->insert_id);
724
- }
725
- // end insert_row
726
-
727
- /**
728
- * Updates an existing row
729
- */
730
- public static function update_row($_data = array(), $_table = ''){
731
- if (empty($_data) || empty($_table)){
732
- return -1;
733
- }
734
-
735
- // Move the ID at the end of the array
736
- $id = $_data['id'];
737
- unset($_data['id']);
738
-
739
- // Remove unwanted characters (SQL injections, anyone?)
740
- $data_keys = array();
741
- foreach (array_keys($_data) as $a_key){
742
- $data_keys[] = sanitize_key($a_key);
743
- }
744
-
745
- // Add the id at the end
746
- $_data['id'] = $id;
747
-
748
- self::$wpdb->query(self::$wpdb->prepare("
749
- UPDATE IGNORE $_table
750
- SET ".implode(' = %s, ', $data_keys)." = %s
751
- WHERE id = %d", $_data));
752
-
753
- return 0;
754
- }
755
-
756
- /**
757
- * Tries to find the user's REAL IP address
758
- */
759
- protected static function _get_remote_ip(){
760
- $ip_array = array( '', '' );
761
-
762
- if ( !empty( $_SERVER[ 'REMOTE_ADDR' ] ) && filter_var( $_SERVER[ 'REMOTE_ADDR' ], FILTER_VALIDATE_IP ) !== false ) {
763
- $ip_array[ 0 ] = $_SERVER[ 'REMOTE_ADDR' ];
764
- }
765
-
766
- $originating_ip_headers = array( 'X-Forwarded-For', 'HTTP_X_FORWARDED_FOR', 'CF-Connecting-IP', 'HTTP_CLIENT_IP', 'HTTP_X_REAL_IP', 'HTTP_FORWARDED', 'HTTP_X_FORWARDED' );
767
- foreach ( $originating_ip_headers as $a_header ) {
768
- if ( !empty( $_SERVER[ $a_header ] ) ) {
769
- foreach ( explode( ',', $_SERVER[ $a_header ] ) as $a_ip ) {
770
- if ( filter_var( $a_ip, FILTER_VALIDATE_IP ) !== false && $a_ip != $ip_array[ 0 ] ) {
771
- $ip_array[ 1 ] = $a_ip;
772
- break;
773
- }
774
- }
775
- }
776
- }
777
-
778
- return $ip_array;
779
- }
780
- // end _get_remote_ip
781
-
782
- /**
783
- * Extracts the accepted language from browser headers
784
- */
785
- protected static function _get_language(){
786
- if(isset($_SERVER["HTTP_ACCEPT_LANGUAGE"])){
787
-
788
- // Capture up to the first delimiter (, found in Safari)
789
- preg_match("/([^,;]*)/", $_SERVER["HTTP_ACCEPT_LANGUAGE"], $array_languages);
790
-
791
- // Fix some codes, the correct syntax is with minus (-) not underscore (_)
792
- return str_replace( "_", "-", strtolower( $array_languages[0] ) );
793
- }
794
- return 'xx'; // Indeterminable language
795
- }
796
- // end _get_language
797
-
798
- /**
799
- * Sniffs out referrals from search engines and tries to determine the query string
800
- */
801
- protected static function _get_search_terms( $_url = array() ) {
802
- if ( empty( $_url ) || !isset( $_url[ 'host' ] ) ) {
803
- return '';
804
- }
805
-
806
- // Engines with different character encodings should always be listed here, regardless of their query string format
807
- $query_formats = array(
808
- 'baidu.com' => 'wd',
809
- 'bing' => 'q',
810
- 'dogpile.com' => 'q',
811
- 'duckduckgo' => 'q',
812
- 'eniro' => 'search_word',
813
- 'exalead.com' => 'q',
814
- 'excite' => 'q',
815
- 'gigablast' => 'q',
816
- 'google' => 'q',
817
- 'hotbot' => 'q',
818
- 'maktoob' => 'p',
819
- 'mamma' => 'q',
820
- 'naver' => 'query',
821
- 'qwant' => 'q',
822
- 'rambler' => 'query',
823
- 'seznam' => 'oq',
824
- 'soso.com' => 'query',
825
- 'virgilio' => 'qs',
826
- 'voila' => 'rdata',
827
- 'yahoo' => 'p',
828
- 'yam' => 'k',
829
- 'yandex' => 'text',
830
- 'yell' => 'keywords',
831
- 'yippy' => 'query',
832
- 'youdao' => 'q'
833
- );
834
-
835
- $charsets = array( 'baidu' => 'EUC-CN' );
836
- $regex_match = implode( '|', array_keys( $query_formats ) );
837
- $searchterms = '';
838
-
839
- if ( !empty( $_url[ 'query' ] ) ) {
840
- parse_str( $_url[ 'query' ], $query );
841
- }
842
-
843
- if ( !empty( $_url[ 'host' ] ) ) {
844
- preg_match( "~($regex_match).~i", $_url[ 'host' ], $matches );
845
- }
846
-
847
- if ( !empty( $matches[ 1 ] ) ) {
848
- // Let's remember that this is a search engine, regardless of the URL containing searchterms (thank you, NSA)
849
- $searchterms = '_';
850
- if ( !empty( $query[ $query_formats[ $matches[ 1 ] ] ] ) ) {
851
- $searchterms = str_replace( '\\', '', trim( urldecode( $query[ $query_formats[ $matches[ 1 ] ] ] ) ) );
852
- // Test for encodings different from UTF-8
853
- if ( function_exists( 'mb_check_encoding' ) && !mb_check_encoding( $query[ $query_formats[ $matches[ 1 ] ] ], 'UTF-8' ) && !empty( $charsets[ $matches[ 1 ] ] ) ) {
854
- $searchterms = mb_convert_encoding( urldecode( $query[ $query_formats[ $matches[ 1 ] ] ] ), 'UTF-8', $charsets[ $matches[ 1 ] ] );
855
- }
856
- }
857
- }
858
- else {
859
- // We weren't lucky, but there's still hope
860
- foreach( array( 'q','s','k','qt' ) as $a_format ) {
861
- if ( !empty( $query[ $a_format ] ) ) {
862
- $searchterms = str_replace( '\\', '', trim( urldecode( $query[ $a_format ] ) ) );
863
- break;
864
- }
865
- }
866
- }
867
-
868
- return $searchterms;
869
- }
870
- // end _get_search_terms
871
-
872
- /**
873
- * Returns details about the resource being accessed
874
- */
875
- protected static function _get_content_info(){
876
- $content_info = array( 'content_type' => 'unknown' );
877
-
878
- // Mark 404 pages
879
- if ( is_404() ) {
880
- $content_info[ 'content_type' ] = '404';
881
- }
882
-
883
- // Type
884
- else if ( is_single() ) {
885
- if ( ( $post_type = get_post_type() ) != 'post' ) {
886
- $post_type = 'cpt:' . $post_type;
887
- }
888
-
889
- $content_info[ 'content_type' ] = $post_type;
890
- $content_info_array = array();
891
- foreach ( get_object_taxonomies( $GLOBALS[ 'post' ] ) as $a_taxonomy ) {
892
- $terms = get_the_terms( $GLOBALS[ 'post' ]->ID, $a_taxonomy );
893
- if ( is_array( $terms ) ) {
894
- foreach ( $terms as $a_term ) {
895
- $content_info_array[] = $a_term->term_id;
896
- }
897
- $content_info[ 'category' ] = implode( ',', $content_info_array );
898
- }
899
- }
900
- $content_info[ 'content_id' ] = $GLOBALS[ 'post' ]->ID;
901
- }
902
- else if ( is_page() ) {
903
- $content_info[ 'content_type' ] = 'page';
904
- $content_info[ 'content_id' ] = $GLOBALS[ 'post' ]->ID;
905
- }
906
- elseif (is_attachment()){
907
- $content_info['content_type'] = 'attachment';
908
- }
909
- elseif (is_singular()){
910
- $content_info['content_type'] = 'singular';
911
- }
912
- elseif (is_post_type_archive()){
913
- $content_info['content_type'] = 'post_type_archive';
914
- }
915
- elseif (is_tag()){
916
- $content_info['content_type'] = 'tag';
917
- $list_tags = get_the_tags();
918
- if (is_array($list_tags)){
919
- $tag_info = array_pop($list_tags);
920
- if (!empty($tag_info)) $content_info['category'] = "$tag_info->term_id";
921
- }
922
- }
923
- elseif (is_tax()){
924
- $content_info['content_type'] = 'taxonomy';
925
- }
926
- elseif (is_category()){
927
- $content_info['content_type'] = 'category';
928
- $list_categories = get_the_category();
929
- if (is_array($list_categories)){
930
- $cat_info = array_pop($list_categories);
931
- if (!empty($cat_info)) $content_info['category'] = "$cat_info->term_id";
932
- }
933
- }
934
- else if (is_date()){
935
- $content_info['content_type']= 'date';
936
- }
937
- else if (is_author()){
938
- $content_info['content_type'] = 'author';
939
- }
940
- else if ( is_archive() ) {
941
- $content_info['content_type'] = 'archive';
942
- }
943
- else if ( is_search() ) {
944
- $content_info[ 'content_type' ] = 'search';
945
- }
946
- else if ( is_feed() ) {
947
- $content_info[ 'content_type' ] = 'feed';
948
- }
949
- else if ( is_home() || is_front_page() ) {
950
- $content_info['content_type'] = 'home';
951
- }
952
- else if ( !empty( $GLOBALS[ 'pagenow' ] ) && $GLOBALS[ 'pagenow' ] == 'wp-login.php' ) {
953
- $content_info[ 'content_type' ] = 'login';
954
- }
955
- else if ( !empty( $GLOBALS['pagenow'] ) && $GLOBALS['pagenow'] == 'wp-register.php' ) {
956
- $content_info[ 'content_type' ] = 'registration';
957
- }
958
- // WordPress sets is_admin() to true for all ajax requests ( front-end or admin-side )
959
- elseif ( is_admin() && ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
960
- $content_info[ 'content_type' ] = 'admin';
961
- }
962
-
963
- if (is_paged()){
964
- $content_info[ 'content_type' ] .= ',paged';
965
- }
966
-
967
- // Author
968
- if ( is_singular() ) {
969
- $author = get_the_author_meta( 'user_login', $GLOBALS[ 'post' ]->post_author );
970
- if ( !empty( $author ) ) {
971
- $content_info[ 'author' ] = $author;
972
- }
973
- }
974
-
975
- return $content_info;
976
- }
977
- // end _get_content_info
978
-
979
- /**
980
- * Reads the cookie to get the visit_id and sets the variable accordingly
981
- */
982
- protected static function _set_visit_id($_force_assign = false){
983
- $is_new_session = true;
984
- $identifier = 0;
985
-
986
- if ( isset( $_COOKIE[ 'slimstat_tracking_code' ] ) ) {
987
- // Make sure only authorized information is recorded
988
- $identifier = self::_separate_id_from_checksum( $_COOKIE[ 'slimstat_tracking_code' ] );
989
- if ( $identifier === false ) {
990
- return false;
991
- }
992
-
993
- $is_new_session = ( strpos( $identifier, 'id' ) !== false );
994
- $identifier = intval( $identifier );
995
- }
996
-
997
- // User doesn't have an active session
998
- if ( $is_new_session && ( $_force_assign || self::$settings[ 'javascript_mode' ] == 'on' ) ) {
999
- if ( empty( self::$settings[ 'session_duration' ] ) ) {
1000
- self::$settings[ 'session_duration' ] = 1800;
1001
- }
1002
-
1003
- self::$stat[ 'visit_id' ] = get_transient( 'slimstat_visit_id' );
1004
- if ( self::$stat[ 'visit_id' ] === false ) {
1005
- self::$stat[ 'visit_id' ] = intval( self::$wpdb->get_var( "SELECT MAX( visit_id ) FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_stats" ) );
1006
- }
1007
- self::$stat[ 'visit_id' ]++;
1008
- set_transient( 'slimstat_visit_id', self::$stat[ 'visit_id' ] );
1009
-
1010
- $set_cookie = apply_filters( 'slimstat_set_visit_cookie', ( !empty( self::$settings[ 'set_tracker_cookie' ] ) && self::$settings[ 'set_tracker_cookie' ] == 'on' ) );
1011
- if ( $set_cookie ) {
1012
- @setcookie(
1013
- 'slimstat_tracking_code',
1014
- self::_get_id_with_checksum( self::$stat[ 'visit_id' ] ),
1015
- time() + self::$settings[ 'session_duration' ],
1016
- COOKIEPATH
1017
- );
1018
- }
1019
-
1020
- }
1021
- elseif ( $identifier > 0 ) {
1022
- self::$stat[ 'visit_id' ] = $identifier;
1023
- }
1024
-
1025
- if ( $is_new_session && $identifier > 0 ) {
1026
- self::$wpdb->query( self::$wpdb->prepare( "
1027
- UPDATE {$GLOBALS['wpdb']->prefix}slim_stats
1028
- SET visit_id = %d
1029
- WHERE id = %d AND visit_id = 0", self::$stat[ 'visit_id' ], $identifier
1030
- ) );
1031
- }
1032
- return ( $is_new_session && ( $_force_assign || self::$settings[ 'javascript_mode' ] == 'on' ) );
1033
- }
1034
- // end _set_visit_id
1035
-
1036
- /**
1037
- * Makes sure that the data received from the client is well-formed (and that nobody is trying to do bad stuff)
1038
- */
1039
- protected static function _check_data_integrity( $_data = '' ) {
1040
- // Parse the information we received
1041
- self::$data_js = apply_filters( 'slimstat_filter_pageview_data_js', $_data );
1042
-
1043
- // Do we have an id for this request?
1044
- if ( empty( self::$data_js[ 'id' ] ) || empty( self::$data_js[ 'op' ] ) ) {
1045
- do_action( 'slimstat_track_exit_102' );
1046
- self::$stat[ 'id' ] = -100;
1047
- self::_set_error_array( __( 'Invalid payload string. Try clearing your WordPress cache.', 'wp-slimstat' ), false );
1048
- self::slimstat_save_options();
1049
- exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
1050
- }
1051
-
1052
- // Make sure that the control code is valid
1053
- self::$data_js[ 'id' ] = self::_separate_id_from_checksum( self::$data_js[ 'id' ] );
1054
-
1055
- if ( self::$data_js['id'] === false ) {
1056
- do_action( 'slimstat_track_exit_103' );
1057
- self::$stat[ 'id' ] = -101;
1058
- self::_set_error_array( __( 'Invalid data signature. Try clearing your WordPress cache.', 'wp-slimstat' ), false );
1059
- self::slimstat_save_options();
1060
- exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
1061
- }
1062
-
1063
- $intval_id = intval( self::$data_js[ 'id' ] );
1064
- if ( $intval_id < 0 ) {
1065
- do_action( 'slimstat_track_exit_' . abs( $intval_id ) );
1066
- self::$stat[ 'id' ] = $intval_id;
1067
- self::slimstat_save_options();
1068
- exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
1069
- }
1070
- }
1071
- // end _check_data_integrity
1072
-
1073
- protected static function _set_error_array( $_error_message = '', $_is_notice = false, $_error_code = 0 ) {
1074
- $error_code = empty( $_error_code ) ? abs( self::$stat[ 'id' ] ) : $_error_code;
1075
- self::toggle_date_i18n_filters( false );
1076
- if ( $_is_notice ) {
1077
- self::$settings[ 'last_tracker_notice' ] = array( $error_code, $_error_message, date_i18n( 'U' ) );
1078
- }
1079
- else {
1080
- self::$settings[ 'last_tracker_error' ] = array( $error_code, $_error_message, date_i18n( 'U' ) );
1081
- }
1082
- self::toggle_date_i18n_filters( true );
1083
- }
1084
-
1085
- protected static function _get_id_with_checksum( $_id = 0 ) {
1086
- return $_id . '.' . md5( $_id . self::$settings[ 'secret' ] );
1087
- }
1088
-
1089
- protected static function _separate_id_from_checksum( $_id_with_checksum = '' ) {
1090
- list( $id, $checksum ) = explode( '.', $_id_with_checksum );
1091
-
1092
- if ( $checksum === md5( $id . self::$settings[ 'secret' ] ) ) {
1093
- return $id;
1094
- }
1095
-
1096
- return false;
1097
- }
1098
-
1099
- protected static function _is_blacklisted( $_needles = array(), $_haystack_string = '', $_return_error_code = array( 0, '', false ) ) {
1100
- foreach ( self::string_to_array( $_haystack_string ) as $a_item ) {
1101
- $pattern = str_replace( array( '\*', '\!' ) , array( '(.*)', '.' ), preg_quote( $a_item, '@' ) );
1102
-
1103
- if ( !is_array( $_needles ) ) {
1104
- $_needles = array( $_needles );
1105
- }
1106
-
1107
- foreach ( $_needles as $a_needle ) {
1108
- if ( preg_match( "@^$pattern$@i", $a_needle ) ) {
1109
-
1110
- self::$stat[ 'id' ] = $_return_error_code[ 0 ];
1111
- self::_set_error_array( $_return_error_code[ 1 ], $_return_error_code[ 2 ] );
1112
- return true;
1113
- }
1114
- }
1115
- }
1116
-
1117
- return false;
1118
- }
1119
-
1120
- public static function is_local_ip_address( $ip_address = '' ) {
1121
- if ( !filter_var( $ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
1122
- return true;
1123
- }
1124
-
1125
- return false;
1126
- }
1127
-
1128
- public static function dtr_pton( $ip ){
1129
- if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
1130
- $unpacked = unpack( 'A4', inet_pton( $ip ) );
1131
- }
1132
- else if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) && defined( 'AF_INET6' ) ) {
1133
- $unpacked = unpack( 'A16', inet_pton( $ip ) );
1134
- }
1135
-
1136
- $binary_ip = '';
1137
- if ( !empty( $unpacked ) ) {
1138
- $unpacked = str_split( $unpacked[ 1 ] );
1139
- foreach ( $unpacked as $char ) {
1140
- $binary_ip .= str_pad( decbin( ord( $char ) ), 8, '0', STR_PAD_LEFT );
1141
- }
1142
- }
1143
-
1144
- return $binary_ip;
1145
- }
1146
-
1147
- public static function get_mask_length( $ip ){
1148
- if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
1149
- return 32;
1150
- }
1151
- else if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
1152
- return 128;
1153
- }
1154
-
1155
- return false;
1156
- }
1157
-
1158
- /**
1159
- * Opens given domains during CORS requests to admin-ajax.php
1160
- */
1161
- public static function open_cors_admin_ajax( $_allowed_origins = array() ) {
1162
- $exploded_domains = self::string_to_array( self::$settings[ 'external_domains' ] );
1163
-
1164
- if ( !empty( $exploded_domains ) && !empty( $exploded_domains[ 0 ] ) ) {
1165
- $_allowed_origins = array_merge( $_allowed_origins, $exploded_domains );
1166
- }
1167
-
1168
- return $_allowed_origins;
1169
- }
1170
-
1171
- /**
1172
- * Downloads the MaxMind geolocation database from their repository
1173
- */
1174
- public static function download_maxmind_database() {
1175
- // Create the folder, if it doesn't exist
1176
- if ( !file_exists( dirname( self::$maxmind_path ) ) ) {
1177
- mkdir( dirname( self::$maxmind_path ) );
1178
- }
1179
-
1180
- if ( file_exists( self::$maxmind_path ) ) {
1181
- if ( is_file( self::$maxmind_path ) ) {
1182
- $is_deleted = @unlink( self::$maxmind_path );
1183
- }
1184
- else {
1185
- // This should not happen, but hey...
1186
- $is_deleted = @rmdir( self::$maxmind_path );
1187
- }
1188
-
1189
- if ( !$is_deleted ) {
1190
- return __( "The geolocation database cannot be updated. Please check your server's file permissions and try again.", 'wp-slimstat' );
1191
- }
1192
- }
1193
-
1194
- // Download the most recent database directly from MaxMind's repository
1195
- if ( self::$settings[ 'geolocation_country' ] == 'on' ) {
1196
- $maxmind_tmp = self::download_url( 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz' );
1197
- }
1198
- else {
1199
- $maxmind_tmp = self::download_url( 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz' );
1200
- }
1201
-
1202
- if ( is_wp_error( $maxmind_tmp ) ) {
1203
- return __( 'There was an error downloading the MaxMind Geolite DB:', 'wp-slimstat' ) . ' ' . $maxmind_tmp->get_error_message();
1204
- }
1205
-
1206
- $zh = false;
1207
-
1208
- if ( !function_exists( 'gzopen' ) ) {
1209
- if ( function_exists( 'gzopen64' ) ) {
1210
- if ( false === ( $zh = gzopen64( $maxmind_tmp, 'rb' ) ) ) {
1211
- return __( 'There was an error opening the zipped MaxMind Geolite DB.', 'wp-slimstat' );
1212
- }
1213
- }
1214
- else {
1215
- return __( 'Function gzopen not defined. Aborting.', 'wp-slimstat' );
1216
- }
1217
- }
1218
- else{
1219
- if ( false === ( $zh = gzopen( $maxmind_tmp, 'rb' ) ) ) {
1220
- return __( 'There was an error opening the zipped MaxMind Geolite DB.', 'wp-slimstat' );
1221
- }
1222
- }
1223
-
1224
- if ( false === ( $fh = fopen( self::$maxmind_path, 'wb' ) ) ) {
1225
- return __( 'There was an error opening the MaxMind Geolite DB.', 'wp-slimstat' );
1226
- }
1227
-
1228
- while ( ( $data = gzread( $zh, 4096 ) ) != false ) {
1229
- fwrite( $fh, $data );
1230
- }
1231
-
1232
- @gzclose( $zh );
1233
- @fclose( $fh );
1234
-
1235
- if ( !is_file( self::$maxmind_path ) ) {
1236
- // Something went wrong, maybe a folder was created instead of a regular file
1237
- @rmdir( self::$maxmind_path );
1238
- return __( 'There was an error creating the MaxMind Geolite DB.', 'wp-slimstat' );
1239
- }
1240
-
1241
- @unlink( $maxmind_tmp );
1242
-
1243
- return '';
1244
- }
1245
-
1246
- public static function download_url( $url ) {
1247
- // Include the FILE API, if it's not defined
1248
- if ( !function_exists( 'download_url' ) ) {
1249
- require_once( ABSPATH . 'wp-admin/includes/file.php' );
1250
- }
1251
-
1252
- if ( !$url ) {
1253
- return new WP_Error('http_no_url', __('Invalid URL Provided.'));
1254
- }
1255
-
1256
- $url_filename = basename( parse_url( $url, PHP_URL_PATH ) );
1257
-
1258
- $tmpfname = wp_tempnam( $url_filename );
1259
- if ( ! $tmpfname ) {
1260
- return new WP_Error('http_no_file', __('Could not create Temporary file.'));
1261
- }
1262
-
1263
- $response = wp_safe_remote_get( $url, array( 'timeout' => 300, 'stream' => true, 'filename' => $tmpfname, 'user-agent' => 'Slimstat Analytics/' . self::$version . '; ' . home_url() ) );
1264
-
1265
- if ( is_wp_error( $response ) ) {
1266
- unlink( $tmpfname );
1267
- return $response;
1268
- }
1269
-
1270
- if ( 200 != wp_remote_retrieve_response_code( $response ) ){
1271
- unlink( $tmpfname );
1272
- return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ) );
1273
- }
1274
-
1275
- return $tmpfname;
1276
- }
1277
-
1278
- public static function slimstat_shortcode( $_attributes = '', $_content = '' ) {
1279
- extract( shortcode_atts( array(
1280
- 'f' => '', // recent, popular, count, widget
1281
- 'w' => '', // column to use (for recent, popular and count) or widget to use
1282
- 's' => ' ', // separator
1283
- 'o' => 0 // offset for counters
1284
- ), $_attributes));
1285
-
1286
- $output = $where = $as_column = '';
1287
- $s = "<span class='slimstat-item-separator'>$s</span>";
1288
-
1289
- // Load the localization files (for languages, operating systems, etc)
1290
- load_plugin_textdomain( 'wp-slimstat', WP_PLUGIN_DIR .'/wp-slimstat/languages', '/wp-slimstat/languages' );
1291
-
1292
- // Look for required fields
1293
- if ( empty( $f ) || empty( $w ) ) {
1294
- return '<!-- Slimstat Shortcode Error: missing parameter -->';
1295
- }
1296
-
1297
- // Include the Reports Library, but don't initialize the database, since we will do that separately later
1298
- include_once( dirname(__FILE__) . '/admin/view/wp-slimstat-reports.php' );
1299
- wp_slimstat_reports::init();
1300
-
1301
- // Init the database library with the appropriate filters
1302
- if ( strpos ( $_content, 'WHERE:' ) !== false ) {
1303
- $where = html_entity_decode( str_replace( 'WHERE:', '', $_content ), ENT_QUOTES, 'UTF-8' );
1304
- // wp_slimstat_db::init();
1305
- }
1306
- else{
1307
- wp_slimstat_db::init( html_entity_decode( $_content, ENT_QUOTES, 'UTF-8' ) );
1308
- }
1309
-
1310
- switch( $f ) {
1311
- case 'count':
1312
- case 'count-all':
1313
- $output = wp_slimstat_db::count_records( $w, $where, strpos( $f, 'all') === false ) + $o;
1314
- break;
1315
-
1316
- case 'widget':
1317
- if ( empty( wp_slimstat_reports::$reports_info[ $w ] ) ) {
1318
- return __( 'Undefined report ID', 'wp-slimstat' );
1319
- }
1320
-
1321
- wp_register_style( 'wp-slimstat-frontend', plugins_url( '/admin/css/slimstat.frontend.css', __FILE__ ) );
1322
- wp_enqueue_style( 'wp-slimstat-frontend' );
1323
-
1324
- ob_start();
1325
- echo wp_slimstat_reports::report_header( $w );
1326
- call_user_func( wp_slimstat_reports::$reports_info[ $w ][ 'callback' ], wp_slimstat_reports::$reports_info[ $w ][ 'callback_args' ] );
1327
- wp_slimstat_reports::report_footer();
1328
- $output = ob_get_contents();
1329
- ob_end_clean();
1330
- break;
1331
-
1332
- case 'recent':
1333
- case 'recent-all':
1334
- case 'top':
1335
- case 'top-all':
1336
- $function = 'get_' . str_replace( '-all', '', $f );
1337
-
1338
- if ( $w == '*' ) {
1339
- $w = 'id';
1340
- }
1341
-
1342
- $w = self::string_to_array( $w );
1343
-
1344
- // Some columns are 'special' and need be removed from the list
1345
- $w_clean = array_diff( $w, array( 'count', 'display_name', 'hostname', 'post_link', 'post_link_no_qs', 'dt' ) );
1346
-
1347
- // The special value 'display_name' requires the username to be retrieved
1348
- if ( in_array( 'display_name', $w ) ) {
1349
- $w_clean[] = 'username';
1350
- }
1351
-
1352
- // The special value 'post_list' requires the resource to be retrieved
1353
- if ( in_array( 'post_link', $w ) ) {
1354
- $w_clean[] = 'resource';
1355
- }
1356
-
1357
- // The special value 'post_list_no_qs' requires a substring to be calculated
1358
- if ( in_array( 'post_link_no_qs', $w ) ) {
1359
- $w_clean = array( 'SUBSTRING_INDEX( resource, "' . ( !get_option( 'permalink_structure' ) ? '&' : '?' ) . '", 1 )' );
1360
- $as_column = 'resource_calculated';
1361
- }
1362
-
1363
- // Retrieve the data
1364
- $results = wp_slimstat_db::$function( implode( ', ', $w_clean ), $where, '', strpos( $f, 'all' ) === false, $as_column );
1365
-
1366
- // No data? No problem!
1367
- if ( empty( $results ) ) {
1368
- return '<!-- Slimstat Shortcode: No Data -->';
1369
- }
1370
-
1371
- // Are nice permalinks enabled?
1372
- $permalinks_enabled = get_option( 'permalink_structure' );
1373
-
1374
- // Format results
1375
- $output = array();
1376
- foreach( $results as $result_idx => $a_result ) {
1377
- foreach( $w as $a_column ) {
1378
- $output[ $result_idx ][ $a_column ] = "<span class='col-$a_column'>";
1379
-
1380
- switch( $a_column ) {
1381
- case 'count':
1382
- $output[ $result_idx ][ $a_column ] .= $a_result[ 'counthits' ];
1383
- break;
1384
-
1385
- case 'country':
1386
- $output[ $result_idx ][ $a_column ] .= __( 'c-' . $a_result[ $a_column ], 'wp-slimstat' );
1387
- break;
1388
-
1389
- case 'display_name':
1390
- $user_details = get_user_by( 'login', $a_result[ 'username' ] );
1391
- if ( !empty( $user_details ) ) {
1392
- $output[ $result_idx ][ $a_column ] .= $user_details->display_name;
1393
- }
1394
- else {
1395
- $output[ $result_idx ][ $a_column ] .= $a_result[ 'username' ];
1396
- }
1397
-
1398
- break;
1399
-
1400
- case 'dt':
1401
- $output[ $result_idx ][ $a_column ] .= date_i18n( self::$settings[ 'date_format' ] . ' ' . self::$settings[ 'time_format' ], $a_result[ 'dt' ] );
1402
- break;
1403
-
1404
- case 'hostname':
1405
- $output[ $result_idx ][ $a_column ] .= gethostbyaddr( $a_result[ 'ip' ] );
1406
- break;
1407
-
1408
- case 'language':
1409
- $output[ $result_idx ][ $a_column ] .= __( 'l-' . $a_result[ $a_column ], 'wp-slimstat' );
1410
- break;
1411
-
1412
- case 'platform':
1413
- $output[ $result_idx ][ $a_column ] .= __( $a_result[ $a_column ], 'wp-slimstat' );
1414
- break;
1415
-
1416
- case 'post_link':
1417
- case 'post_link_no_qs':
1418
- $resource_key = ( $a_column == 'post_link' ) ? 'resource' : 'resource_calculated';
1419
- $post_id = url_to_postid( $a_result[ $resource_key ] );
1420
- if ( $post_id > 0 ) {
1421
- $output[ $result_idx ][ $a_column ] .= "<a href='{$a_result[ $resource_key ]}'>" . get_the_title( $post_id ) . '</a>';
1422
- }
1423
- else {
1424
- $output[ $result_idx ][ $a_column ] .= "<a href='{$a_result[ $resource_key ]}'>{$a_result[ $resource_key ]}</a>";
1425
- }
1426
- break;
1427
-
1428
- default:
1429
- $output[ $result_idx ][ $a_column ] .= $a_result[ $a_column ];
1430
- break;
1431
- }
1432
- $output[ $result_idx ][ $a_column ] .= '</span>';
1433
- }
1434
- $output[ $result_idx ] = '<li>' . implode( $s, $output[ $result_idx ] ). '</li>';
1435
- }
1436
-
1437
- $output = '<ul class="slimstat-shortcode ' . $f . implode( '-', $w ). '">' . implode( '', $output ) . '</ul>';
1438
- break;
1439
-
1440
- default:
1441
- break;
1442
- }
1443
-
1444
- return $output;
1445
- }
1446
-
1447
- public static function rest_api_response( $_request = array() ) {
1448
- $filters = '';
1449
- if ( !empty( $_request[ 'filters' ] ) ) {
1450
- $filters = $_request[ 'filters' ];
1451
- }
1452
-
1453
- if ( empty( $_request[ 'dimension' ] ) ) {
1454
- return new WP_Error( 'rest_invalid', esc_html__( 'The dimension parameter is required. Please review your request and try again.', 'wp-slimstat' ), array( 'status' => 400 ) );
1455
- }
1456
-
1457
- if ( empty( $_request[ 'function' ] ) ) {
1458
- return new WP_Error( 'rest_invalid', esc_html__( 'The function parameter is required. Please review your request and try again.', 'wp-slimstat' ), array( 'status' => 400 ) );
1459
- }
1460
-
1461
- include_once( dirname(__FILE__) . '/admin/view/wp-slimstat-db.php' );
1462
- wp_slimstat_db::init( $filters );
1463
-
1464
- $response = array(
1465
- 'function' => htmlentities( $_request[ 'function' ], ENT_QUOTES, 'UTF-8' ),
1466
- 'dimension' => htmlentities( $_request[ 'dimension' ], ENT_QUOTES, 'UTF-8' ),
1467
-
1468
- 'data' => 0
1469
- );
1470
-
1471
- switch( $_request[ 'function' ] ) {
1472
- case 'count':
1473
- case 'count-all':
1474
- $response[ 'data' ] = wp_slimstat_db::count_records( $_request[ 'dimension' ], '', strpos( $_request[ 'function' ], '-all') === false );
1475
- break;
1476
-
1477
- case 'recent':
1478
- case 'recent-all':
1479
- case 'top':
1480
- case 'top-all':
1481
- $function = 'get_' . str_replace( '-all', '', $_request[ 'function' ] );
1482
-
1483
- // Retrieve the data
1484
- $response[ 'data' ] = array_values( wp_slimstat_db::$function( $_request[ 'dimension' ], '', '', strpos( $_request[ 'function' ], '-all' ) === false ) );
1485
- break;
1486
-
1487
- default:
1488
- // This should never happen, because of the 'enum' condition for this parameter. But never say never...
1489
- $response[ 'data' ] = new WP_Error( 'rest_invalid', esc_html__( 'Valid function values are count, count-all, recent, recent-all, top and top-all. Please review your request and try again.', 'wp-slimstat' ), array( 'status' => 400 ) );
1490
- break;
1491
- }
1492
-
1493
- return rest_ensure_response( $response );
1494
- }
1495
-
1496
- public static function rest_api_authorization( $_request = array() ) {
1497
- if ( empty( $_request[ 'token' ] ) ) {
1498
- return new WP_Error( 'rest_invalid', esc_html__( 'Please specify a valid token in order to access this REST API.', 'wp-slimstat' ), array( 'status' => 400 ) );
1499
- }
1500
-
1501
- if ( !in_array( $_request[ 'token' ], self::string_to_array( self::$settings['rest_api_tokens'] ) ) ) {
1502
- return false;
1503
- }
1504
-
1505
- return true;
1506
- }
1507
-
1508
- public static function register_rest_route() {
1509
- register_rest_route( 'slimstat/v1', '/get', array(
1510
- 'methods' => WP_REST_Server::READABLE,
1511
- 'callback' => array( __CLASS__, 'rest_api_response' ),
1512
- 'permission_callback' => array( __CLASS__, 'rest_api_authorization' ),
1513
- 'args' => array(
1514
- 'token' => array(
1515
- 'description' => __( 'You will need to specify a valid token to be able to query the data. Tokens are defined in Slimstat > Settings > Access Control.', 'wp-slimstat' ),
1516
- 'type' => 'string'
1517
- ),
1518
- 'function' => array(
1519
- 'description' => __( 'This parameter specifies the type of QUERY for the dimension. Valid values are: count, count-all, recent, recent-all, top and top-all.', 'wp-slimstat' ),
1520
- 'type' => 'string',
1521
- 'enum' => array( 'count', 'count-all', 'recent', 'recent-all', 'top', 'top-all' )
1522
- ),
1523
- 'dimension' => array(
1524
- 'description' => __( 'This parameter indicates what dimension to return: * (all data), ip, resource, browser, operating system, etc. You can only specify one dimension at a time.', 'wp-slimstat' ),
1525
- 'type' => 'string',
1526
- 'enum' => array( '*', 'id', 'ip', 'username', 'country', 'referer', 'resource', 'searchterms', 'browser', 'platform', 'language', 'resolution', 'content_type', 'content_id', 'outbound_resource' )
1527
- ),
1528
- 'filters' => array(
1529
- 'description' => __( 'This parameter is used to filter a given dimension (resources, browsers, operating systems, etc) so that it satisfies certain conditions (i.e.: browser contains Chrome). Please make sure to urlencode this value, and to use the usual filter format: browser contains Chrome&&&referer contains slim (encoded: browser%20contains%20Chrome%26%26%26referer%20contains%20slim)', 'wp-slimstat' ),
1530
- 'type' => 'string'
1531
- )
1532
- )
1533
- ) );
1534
- }
1535
-
1536
- /**
1537
- * Converts a series of comma separated values into an array
1538
- */
1539
- public static function string_to_array( $_option = '' ) {
1540
- if ( empty( $_option ) || !is_string( $_option ) ) {
1541
- return array();
1542
- }
1543
- else {
1544
- return array_filter( array_map( 'trim', explode( ',', $_option ) ) );
1545
- }
1546
- }
1547
-
1548
- /**
1549
- * Toggles WordPress filters on date_i18n function
1550
- */
1551
- public static function toggle_date_i18n_filters( $_turn_on = true ) {
1552
- if ( $_turn_on && !empty( self::$date_i18n_filters ) && is_array( self::$date_i18n_filters ) ) {
1553
- foreach ( self::$date_i18n_filters as $i18n_priority => $i18n_func_list ) {
1554
- foreach ( $i18n_func_list as $func_name => $func_args ) {
1555
- if ( !empty( $func_args[ 'function' ] ) && is_string( $func_args[ 'function' ] ) ) {
1556
- add_filter( 'date_i8n', $func_args[ 'function' ], $i18n_priority, intval( $func_args[ 'accepted_args' ] ) );
1557
- }
1558
- }
1559
- }
1560
- }
1561
- else if ( !empty( $GLOBALS[ 'wp_filter' ][ 'date_i18n' ][ 'callbacks' ] ) && is_array( $GLOBALS[ 'wp_filter' ][ 'date_i18n' ][ 'callbacks' ] ) ) {
1562
- self::$date_i18n_filters = $GLOBALS[ 'wp_filter' ][ 'date_i18n' ][ 'callbacks' ];
1563
- remove_all_filters( 'date_i18n' );
1564
- }
1565
- }
1566
-
1567
- /**
1568
- * Imports all the 'old' options into the new array, and saves them
1569
- */
1570
- public static function init_options(){
1571
- return array(
1572
- 'version' => self::$version,
1573
- 'secret' => wp_hash( uniqid( time(), true ) ),
1574
- 'show_admin_notice' => 0,
1575
- 'browscap_last_modified' => 0,
1576
-
1577
- // General
1578
- 'is_tracking' => 'on',
1579
- 'javascript_mode' => 'on',
1580
- 'enable_javascript' => 'on',
1581
- 'track_admin_pages' => 'no',
1582
- 'use_separate_menu' => 'on',
1583
- 'add_posts_column' => 'no',
1584
- 'posts_column_day_interval' => 30,
1585
- 'posts_column_pageviews' => 'on',
1586
- 'add_dashboard_widgets' => 'on',
1587
- 'hide_addons' => 'no',
1588
- 'auto_purge' => 0,
1589
- 'auto_purge_delete' => 'on',
1590
-
1591
- // Tracker
1592
- 'do_not_track_outbound_classes_rel_href' => 'noslimstat,ab-item',
1593
- 'extensions_to_track' => 'pdf,doc,xls,zip',
1594
- 'track_same_domain_referers' => 'on',
1595
- 'geolocation_country' => 'on',
1596
- 'session_duration' => 1800,
1597
- 'extend_session' => 'no',
1598
- 'set_tracker_cookie' => 'on',
1599
- 'enable_cdn' => 'on',
1600
- 'ajax_relative_path' => 'no',
1601
- 'external_domains' => '',
1602
-
1603
- // Filters
1604
- 'track_users' => 'on',
1605
- 'ignore_spammers' => 'on',
1606
- 'ignore_bots' => 'no',
1607
- 'ignore_prefetch' => 'on',
1608
- 'anonymize_ip' => 'no',
1609
-
1610
- 'ignore_users' => '',
1611
- 'ignore_ip' => '',
1612
- 'ignore_countries' => '',
1613
- 'ignore_browsers' => '',
1614
- 'ignore_platforms' => '',
1615
- 'ignore_capabilities' => '',
1616
-
1617
- 'ignore_resources' => '',
1618
- 'ignore_referers' => '',
1619
- 'ignore_content_types' => '',
1620
-
1621
- // Reports
1622
- 'use_european_separators' => 'on',
1623
- 'date_format' => 'm-d-y',
1624
- 'time_format' => 'h:i a',
1625
- 'show_display_name' => 'no',
1626
- 'convert_resource_urls_to_titles' => 'on',
1627
- 'convert_ip_addresses' => 'no',
1628
- 'async_load' => 'no',
1629
- 'use_current_month_timespan' => 'no',
1630
- 'expand_details' => 'no',
1631
- 'rows_to_show' => '20',
1632
- 'limit_results' => '1000',
1633
- 'ip_lookup_service' => 'http://www.infosniper.net/?ip_address=',
1634
- 'mozcom_access_id' => '',
1635
- 'mozcom_secret_key' => '',
1636
- 'refresh_interval' => '60',
1637
- 'number_results_raw_data' => '50',
1638
- 'max_dots_on_map' => '50',
1639
- 'custom_css' => '',
1640
- 'chart_colors' => '',
1641
- 'show_complete_user_agent_tooltip' => 'no',
1642
- 'enable_sov' => 'no',
1643
-
1644
- // Access Control
1645
- 'restrict_authors_view' => 'on',
1646
- 'capability_can_view' => 'activate_plugins',
1647
- 'can_view' => '',
1648
- 'rest_api_tokens' => wp_hash( uniqid( time() - 3600, true ) ),
1649
- 'capability_can_admin' => 'activate_plugins',
1650
- 'can_admin' => '',
1651
-
1652
- // Maintenance
1653
- 'last_tracker_error' => array( 0, '', 0 ),
1654
- 'show_sql_debug' => 'no',
1655
- 'no_maxmind_warning' => 'no',
1656
- 'no_browscap_warning' => 'no',
1657
-
1658
- // Network-wide Settings
1659
- 'locked_options' => ''
1660
- );
1661
- }
1662
- // end init_options
1663
-
1664
- /**
1665
- * Saves the options in the database, if necessary
1666
- */
1667
- public static function slimstat_save_options() {
1668
- // Allow third-party functions to manipulate the options right before they are saved
1669
- self::$settings = apply_filters( 'slimstat_save_options', self::$settings );
1670
-
1671
- if ( self::$settings_signature === md5( serialize( self::$settings ) ) ) {
1672
- return true;
1673
- }
1674
-
1675
- if ( !is_network_admin() ) {
1676
- update_option( 'slimstat_options', self::$settings );
1677
- }
1678
- else {
1679
- update_site_option( 'slimstat_options', self::$settings );
1680
- }
1681
-
1682
- return true;
1683
- }
1684
-
1685
- /**
1686
- * Enqueue a javascript to track users' screen resolution and other browser-based information
1687
- */
1688
- public static function wp_slimstat_enqueue_tracking_script() {
1689
- // Do not enqueue the script if the corresponding options are turned off
1690
- $is_tracking_filter_js = apply_filters( 'slimstat_filter_pre_tracking_js', true );
1691
- if ( ( self::$settings[ 'enable_javascript' ] != 'on' && self::$settings[ 'javascript_mode' ] != 'on' ) || self::$settings[ 'is_tracking' ] != 'on' || !$is_tracking_filter_js ) {
1692
- return 0;
1693
- }
1694
-
1695
- // Register the script in WordPress
1696
- $jstracker_suffix = ( defined( 'SCRIPT_DEBUG' ) && is_bool( SCRIPT_DEBUG ) && SCRIPT_DEBUG ) ? '' : '.min';
1697
-
1698
- // Pass some information to the tracker
1699
- $params = array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) );
1700
-
1701
- if ( self::$settings[ 'ajax_relative_path' ] == 'on' ) {
1702
- $params[ 'ajaxurl' ] = admin_url( 'admin-ajax.php', 'relative' );
1703
- }
1704
-
1705
- if ( !empty( self::$settings[ 'extensions_to_track' ] ) ) {
1706
- $params[ 'extensions_to_track' ] = str_replace( ' ', '', self::$settings[ 'extensions_to_track' ] );
1707
- }
1708
- if ( !empty( self::$settings[ 'do_not_track_outbound_classes_rel_href' ] ) ) {
1709
- $params[ 'outbound_classes_rel_href_to_not_track' ] = str_replace( ' ', '', self::$settings[ 'do_not_track_outbound_classes_rel_href' ] );
1710
- }
1711
-
1712
- if ( self::$settings[ 'javascript_mode' ] != 'on' ) {
1713
- // Do not enqueue the tracker if this page view was not tracked for some reason
1714
- if ( intval( self::$stat[ 'id' ] ) < 0 ) {
1715
- return false;
1716
- }
1717
-
1718
- if ( !empty( self::$stat[ 'id' ] ) ) {
1719
- $params[ 'id' ] = self::_get_id_with_checksum( self::$stat[ 'id' ] );
1720
- }
1721
- else {
1722
- $params[ 'id' ] = self::_get_id_with_checksum( '-300' );
1723
- }
1724
- }
1725
- else {
1726
- // Check filters and blacklists to see if this page should be tracked
1727
- $simulate = self::slimtrack( array( 'slimtrack_simulate' => true ) );
1728
- if ( empty( $simulate[ 'slimtrack_would_track' ] ) ) {
1729
- return false;
1730
- }
1731
-
1732
- $encoded_ci = base64_encode( serialize( self::_get_content_info() ) );
1733
- $params[ 'ci' ] = self::_get_id_with_checksum( $encoded_ci );
1734
- }
1735
-
1736
- $params = apply_filters( 'slimstat_js_params', $params );
1737
-
1738
- if ( self::$settings[ 'enable_cdn' ] == 'on' ) {
1739
- $schema = is_ssl() ? 'https' : 'http';
1740
- wp_register_script( 'wp_slimstat', $schema . '://cdn.jsdelivr.net/wp/wp-slimstat/tags/' . self::$version . '/wp-slimstat.min.js', array(), null, true );
1741
- }
1742
- else{
1743
- wp_register_script( 'wp_slimstat', plugins_url( "/wp-slimstat{$jstracker_suffix}.js", __FILE__ ), array(), null, true );
1744
- }
1745
-
1746
- wp_enqueue_script( 'wp_slimstat' );
1747
- wp_localize_script( 'wp_slimstat', 'SlimStatParams', $params );
1748
- }
1749
-
1750
- /**
1751
- * Removes old entries from the main table and performs other daily tasks
1752
- */
1753
- public static function wp_slimstat_purge() {
1754
- $autopurge_interval = intval( self::$settings[ 'auto_purge' ] );
1755
-
1756
- if ( $autopurge_interval <= 0 ) {
1757
- return;
1758
- }
1759
-
1760
- self::toggle_date_i18n_filters( false );
1761
- $days_ago = strtotime( date_i18n( 'Y-m-d H:i:s' ) . " -$autopurge_interval days" );
1762
- self::toggle_date_i18n_filters( true );
1763
-
1764
- // Copy entries to the archive table, if needed
1765
- if ( self::$settings[ 'auto_purge_delete' ] != 'no' ) {
1766
- $is_copy_done = self::$wpdb->query("
1767
- INSERT INTO {$GLOBALS['wpdb']->prefix}slim_stats_archive (id, ip, other_ip, username, country, location, city, referer, resource, searchterms, plugins, notes, visit_id, server_latency, page_performance, browser, browser_version, browser_type, platform, language, user_agent, resolution, screen_width, screen_height, content_type, category, author, content_id, outbound_resource, dt_out, dt)
1768
- SELECT id, ip, other_ip, username, country, location, city, referer, resource, searchterms, plugins, notes, visit_id, server_latency, page_performance, browser, browser_version, browser_type, platform, language, user_agent, resolution, screen_width, screen_height, content_type, category, author, content_id, outbound_resource, dt_out, dt
1769
- FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_stats
1770
- WHERE dt < $days_ago");
1771
-
1772
- if ( $is_copy_done !== false ) {
1773
- self::$wpdb->query("DELETE ts FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_stats ts WHERE ts.dt < $days_ago");
1774
- }
1775
-
1776
- $is_copy_done = self::$wpdb->query("
1777
- INSERT INTO {$GLOBALS['wpdb']->prefix}slim_events_archive (type, event_description, notes, position, id, dt)
1778
- SELECT type, event_description, notes, position, id, dt
1779
- FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_events
1780
- WHERE dt < $days_ago");
1781
-
1782
- if ( $is_copy_done !== false ) {
1783
- self::$wpdb->query("DELETE te FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_events te WHERE te.dt < $days_ago");
1784
- }
1785
- }
1786
- else {
1787
- // Delete old entries
1788
- self::$wpdb->query("DELETE ts FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_stats ts WHERE ts.dt < $days_ago");
1789
- self::$wpdb->query("DELETE te FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_events te WHERE te.dt < $days_ago");
1790
- }
1791
-
1792
- // Optimize tables
1793
- self::$wpdb->query( "OPTIMIZE TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_stats" );
1794
- self::$wpdb->query( "OPTIMIZE TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_stats_archive" );
1795
- self::$wpdb->query( "OPTIMIZE TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_events" );
1796
- self::$wpdb->query( "OPTIMIZE TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_events_archive" );
1797
- }
1798
-
1799
- /**
1800
- * Checks for add-on updates
1801
- */
1802
- public static function update_checker() {
1803
- if ( empty( self::$update_checker ) || !is_admin() ) {
1804
- return true;
1805
- }
1806
-
1807
- $update_checker_objects = array();
1808
-
1809
- // This is only included if add-ons are installed
1810
- include_once( plugin_dir_path( __FILE__ ) . 'admin/update-checker/plugin-update-checker.php' );
1811
-
1812
- foreach ( self::$update_checker as $a_slug ) {
1813
- $a_clean_slug = str_replace( array( 'wp_slimstat_', '_' ), array( '', '-' ), $a_slug );
1814
-
1815
- if ( !empty( self::$settings[ 'addon_licenses' ][ 'wp-slimstat-' . $a_clean_slug ] ) ) {
1816
- $update_checker_objects[ $a_clean_slug ] = Puc_v4_Factory::buildUpdateChecker( 'http://www.wp-slimstat.com/update-checker/?slug=' . $a_clean_slug . '&key=' . urlencode( self::$settings[ 'addon_licenses' ][ 'wp-slimstat-' . $a_clean_slug ] ), dirname( dirname( __FILE__ ) ) . '/wp-slimstat-' . $a_clean_slug . '/index.php', 'wp-slimstat-' . $a_clean_slug );
1817
-
1818
- add_filter( "plugin_action_links_wp-slimstat-{$a_clean_slug}/index.php", array( __CLASS__, 'add_plugin_manual_download_link' ), 10, 2 );
1819
- }
1820
- }
1821
- }
1822
-
1823
- public static function add_plugin_manual_download_link( $_links = array(), $_plugin_file = '' ) {
1824
- $a_clean_slug = str_replace( array( 'wp-slimstat-', '/index.php' ), array( '', '' ), $_plugin_file );
1825
-
1826
- if ( false !== ( $download_url = get_transient( 'wp-slimstat-download-link-' . $a_clean_slug ) ) ) {
1827
- $_links[] = '<a href="' . $download_url . '">Download ZIP</a>';
1828
- }
1829
- else {
1830
- $url = 'http://www.wp-slimstat.com/update-checker/?slug=' . $a_clean_slug . '&key=' . urlencode( self::$settings[ 'addon_licenses' ][ 'wp-slimstat-' . $a_clean_slug ] );
1831
- $response = wp_safe_remote_get( $url, array( 'timeout' => 300, 'user-agent' => 'Slimstat Analytics/' . self::$version . '; ' . home_url() ) );
1832
-
1833
- if ( !is_wp_error( $response ) && 200 == wp_remote_retrieve_response_code( $response ) ) {
1834
- $data = @json_decode( $response[ 'body' ] );
1835
-
1836
- if ( is_object( $data ) ) {
1837
- $_links[] = '<a href="' . $data->download_url . '">Download ZIP</a>';
1838
- set_transient( 'wp-slimstat-download-link-' . $a_clean_slug, $data->download_url, 172800 ); // 48 hours
1839
- }
1840
- }
1841
- }
1842
-
1843
- return $_links;
1844
- }
1845
-
1846
- public static function register_widget() {
1847
- return register_widget( "slimstat_widget" );
1848
- }
1849
- }
1850
- // end of class declaration
1851
-
1852
- class slimstat_widget extends WP_Widget {
1853
-
1854
- /**
1855
- * Sets up the widgets name etc
1856
- */
1857
- public function __construct() {
1858
- parent::__construct( 'slimstat_widget', 'SlimStat', array(
1859
- 'classname' => 'slimstat_widget',
1860
- 'description' => 'Add a SlimStat report to your sidebar',
1861
- ) );
1862
- }
1863
-
1864
- /**
1865
- * Outputs the content of the widget
1866
- *
1867
- * @param array $args
1868
- * @param array $instance
1869
- */
1870
- public function widget( $args, $instance ) {
1871
- extract( $instance );
1872
-
1873
- $slimstat_widget_filters = empty( $slimstat_widget_filters ) ? '' : $slimstat_widget_filters;
1874
-
1875
- if ( !empty( $slimstat_widget_id ) ) {
1876
- echo do_shortcode( "[slimstat f='widget' w='{$slimstat_widget_id}']{$slimstat_widget_filters}[/slimstat]" );
1877
- }
1878
- else {
1879
- echo '';
1880
- }
1881
- }
1882
-
1883
- /**
1884
- * Outputs the options form on admin
1885
- *
1886
- * @param array $instance The widget options
1887
- */
1888
- public function form( $instance ) {
1889
- // Let's build the dropdown
1890
- include_once( dirname(__FILE__) . '/admin/view/wp-slimstat-reports.php' );
1891
- wp_slimstat_reports::init();
1892
- $select_options = '';
1893
- $slimstat_widget_id = !empty( $instance[ 'slimstat_widget_id' ] ) ? $instance[ 'slimstat_widget_id' ] : '';
1894
- $slimstat_widget_filters = !empty( $instance[ 'slimstat_widget_filters' ] ) ? $instance[ 'slimstat_widget_filters' ] : '';
1895
-
1896
- foreach ( wp_slimstat_reports::$reports_info as $a_report_id => $a_report_info ) {
1897
- $select_options .= "<option value='$a_report_id' " . ( ( $slimstat_widget_id == $a_report_id ) ? 'selected="selected"' : '' ) . ">{$a_report_info[ 'title' ]}</option>";
1898
- }
1899
- ?>
1900
-
1901
- <p>
1902
- <label for="<?php echo esc_attr( $this->get_field_id( 'slimstat_widget_id' ) ); ?>">Widget</label>
1903
- <select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'slimstat_widget_id' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'slimstat_widget_id' ) ); ?>">
1904
- <option value="">Select a widget</option>
1905
- <?php echo $select_options ?>
1906
- </select>
1907
- </p>
1908
-
1909
- <p>
1910
- <label for="<?php echo esc_attr( $this->get_field_id( 'slimstat_widget_filters' ) ); ?>"><?php _e( 'Optional filters', 'wp-slimstat' ); ?></label>
1911
- <a href="https://slimstat.freshdesk.com/solution/articles/5000631833-what-is-the-syntax-of-a-slimstat-shortcode-#slimstat-operators" target="_blank">[?]</a>
1912
- <textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'slimstat_widget_filters' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'slimstat_widget_filters' ) ); ?>"><?php echo trim( strip_tags( $slimstat_widget_filters ) ) ?></textarea>
1913
- </p>
1914
- <?php
1915
- }
1916
-
1917
- /**
1918
- * Processing widget options on save
1919
- *
1920
- * @param array $new_instance The new options
1921
- * @param array $old_instance The previous options
1922
- */
1923
- public function update( $new_instance, $old_instance ) {
1924
- $instance = $old_instance;
1925
-
1926
- $instance[ 'slimstat_widget_id' ] = $new_instance[ 'slimstat_widget_id' ];
1927
- $instance[ 'slimstat_widget_filters' ] = $new_instance[ 'slimstat_widget_filters' ];
1928
- return $instance;
1929
- }
1930
- }
1931
-
1932
- // Ok, let's go, Sparky!
1933
- if ( function_exists( 'add_action' ) ) {
1934
- // Since we use sendBeacon, this function sends raw POST data, which does not populate the $_POST variable automatically
1935
- if ( ( !empty( $_SERVER[ 'HTTP_CONTENT_TYPE' ] ) || !empty( $_SERVER[ 'CONTENT_TYPE' ] ) ) && empty( $_POST ) ) {
1936
- $raw_post_string = file_get_contents( 'php://input' );
1937
- parse_str( $raw_post_string, wp_slimstat::$raw_post_array );
1938
- }
1939
- else if ( !empty( $_POST ) ) {
1940
- wp_slimstat::$raw_post_array = $_POST;
1941
- }
1942
-
1943
- // Init the Ajax listener
1944
- if ( !empty( wp_slimstat::$raw_post_array[ 'action' ] ) && wp_slimstat::$raw_post_array[ 'action' ] == 'slimtrack' ) {
1945
-
1946
- // This is needed because admin-ajax.php is reading $_REQUEST to fire the corresponding action
1947
- if ( empty( $_POST[ 'action' ] ) ) {
1948
- $_POST[ 'action' ] = wp_slimstat::$raw_post_array[ 'action' ];
1949
- }
1950
-
1951
- add_action( 'wp_ajax_nopriv_slimtrack', array( 'wp_slimstat', 'slimtrack_ajax' ) );
1952
- add_action( 'wp_ajax_slimtrack', array( 'wp_slimstat', 'slimtrack_ajax' ) );
1953
- }
1954
-
1955
- // From the codex: You can't call register_activation_hook() inside a function hooked to the 'plugins_loaded' or 'init' hooks (or any other hook). These hooks are called before the plugin is loaded or activated.
1956
- if ( is_admin() ) {
1957
- include_once( WP_PLUGIN_DIR . '/wp-slimstat/admin/wp-slimstat-admin.php' );
1958
- register_activation_hook( __FILE__, array( 'wp_slimstat_admin', 'init_environment' ) );
1959
- register_deactivation_hook( __FILE__, array( 'wp_slimstat_admin', 'deactivate' ) );
1960
- }
1961
-
1962
- add_action( 'widgets_init', array( 'wp_slimstat', 'register_widget' ) );
1963
-
1964
- // Add the appropriate actions
1965
- add_action( 'plugins_loaded', array( 'wp_slimstat', 'init' ), 20 );
1966
- }
 
1
+ <?php
2
+ /*
3
+ Plugin Name: Slimstat Analytics
4
+ Plugin URI: http://wordpress.org/plugins/wp-slimstat/
5
+ Description: The leading web analytics plugin for WordPress
6
+ Version: 4.7.7
7
+ Author: Jason Crouse
8
+ Author URI: http://www.wp-slimstat.com/
9
+ Text Domain: wp-slimstat
10
+ Domain Path: /languages
11
+ */
12
+
13
+ if ( !empty( wp_slimstat::$settings ) ) {
14
+ return true;
15
+ }
16
+
17
+ class wp_slimstat {
18
+ public static $version = '4.7.7';
19
+ public static $settings = array();
20
+
21
+ public static $wpdb = '';
22
+ public static $upload_dir = '';
23
+ public static $maxmind_path = '';
24
+
25
+ public static $update_checker = array();
26
+ public static $raw_post_array = array();
27
+
28
+ protected static $data_js = array( 'id' => 0 );
29
+ protected static $stat = array();
30
+ protected static $settings_signature = '';
31
+ protected static $browser = array();
32
+ protected static $heuristic_key = 0;
33
+ protected static $date_i18n_filters = array();
34
+
35
+ /**
36
+ * Initializes variables and actions
37
+ */
38
+ public static function init() {
39
+
40
+ // Load all the settings
41
+ if ( is_network_admin() && ( empty($_GET[ 'page' ] ) || strpos( $_GET[ 'page' ], 'slimview' ) === false ) ) {
42
+ self::$settings = get_site_option( 'slimstat_options', array() );
43
+ }
44
+ else {
45
+ self::$settings = get_option( 'slimstat_options', array() );
46
+ }
47
+
48
+ self::$settings = array_merge( self::init_options(), self::$settings );
49
+
50
+ // Allow third party tools to edit the options
51
+ self::$settings = apply_filters( 'slimstat_init_options', self::$settings );
52
+
53
+ // Determine the options' signature: if it hasn't changed, there's no need to update/save them in the database
54
+ self::$settings_signature = md5( serialize( self::$settings ) );
55
+
56
+ // Allow third-party tools to use a custom database for Slimstat
57
+ self::$wpdb = apply_filters( 'slimstat_custom_wpdb', $GLOBALS[ 'wpdb' ] );
58
+
59
+ // Hook a DB clean-up routine to the daily cronjob
60
+ add_action( 'wp_slimstat_purge', array( __CLASS__, 'wp_slimstat_purge' ) );
61
+
62
+ // Allow external domains on CORS requests
63
+ add_filter( 'allowed_http_origins', array(__CLASS__, 'open_cors_admin_ajax' ) );
64
+
65
+ // Define the folder where to store the geolocation database (shared among sites in a network, by default)
66
+ self::$upload_dir = wp_upload_dir();
67
+ self::$upload_dir = self::$upload_dir[ 'basedir' ];
68
+ if ( is_multisite() && ! ( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
69
+ self::$upload_dir = str_replace( '/sites/' . get_current_blog_id(), '', self::$upload_dir );
70
+ }
71
+ self::$upload_dir .= '/wp-slimstat';
72
+ self::$upload_dir = apply_filters( 'slimstat_maxmind_path', self::$upload_dir );
73
+
74
+ self::$maxmind_path = self::$upload_dir . '/maxmind.mmdb';
75
+
76
+ // Enable the tracker (both server- and client-side)
77
+ if ( !is_admin() || self::$settings[ 'track_admin_pages' ] == 'on' ) {
78
+
79
+ // Allow add-ons to turn off the tracker based on other conditions
80
+ $is_tracking_filter = apply_filters( 'slimstat_filter_pre_tracking', strpos( self::get_request_uri(), 'wp-admin/admin-ajax.php' ) === false );
81
+ $is_tracking_filter_js = apply_filters( 'slimstat_filter_pre_tracking_js', true );
82
+
83
+ // Is server-side tracking active?
84
+ if ( self::$settings[ 'javascript_mode' ] != 'on' && self::$settings[ 'is_tracking' ] == 'on' && $is_tracking_filter ) {
85
+ add_action( is_admin() ? 'admin_init' : 'wp', array( __CLASS__, 'slimtrack' ), 5 );
86
+
87
+ if ( self::$settings[ 'track_users' ] == 'on' ) {
88
+ add_action( 'login_init', array( __CLASS__, 'slimtrack' ), 10 );
89
+ }
90
+ }
91
+
92
+ // Slimstat tracks screen resolutions, outbound links and other client-side information using a client-side tracker
93
+ add_action( is_admin() ? 'admin_enqueue_scripts' : 'wp_enqueue_scripts' , array( __CLASS__, 'wp_slimstat_enqueue_tracking_script' ), 15 );
94
+ if ( self::$settings[ 'track_users' ] == 'on' ) {
95
+ add_action( 'login_enqueue_scripts', array( __CLASS__, 'wp_slimstat_enqueue_tracking_script' ), 10 );
96
+ }
97
+ }
98
+
99
+ // Shortcodes
100
+ add_shortcode('slimstat', array(__CLASS__, 'slimstat_shortcode'), 15);
101
+
102
+ // Load the admin library
103
+ if ( is_user_logged_in() ) {
104
+ include_once ( plugin_dir_path( __FILE__ ) . 'admin/wp-slimstat-admin.php' );
105
+ add_action( 'init', array( 'wp_slimstat_admin', 'init' ), 60 );
106
+ }
107
+
108
+ // Include our browser detector library
109
+ include_once( plugin_dir_path( __FILE__ ) . 'browscap/browser.php' );
110
+ add_action( 'init', array( 'slim_browser', 'init' ) );
111
+
112
+ // If add-ons are installed, check for updates
113
+ add_action( 'wp_loaded', array( __CLASS__, 'update_checker' ) );
114
+
115
+ // Update the options before shutting down
116
+ add_action( 'shutdown', array( __CLASS__, 'slimstat_save_options' ), 100 );
117
+
118
+ // REST API Support
119
+ add_action( 'rest_api_init', array( __CLASS__, 'register_rest_route' ) );
120
+ }
121
+ // end init
122
+
123
+ /**
124
+ * Ajax Tracking
125
+ */
126
+ public static function slimtrack_ajax() {
127
+ // If the website is using a caching plugin, the tracking code might still be there, even if the user turned off tracking
128
+ if ( self::$settings[ 'is_tracking' ] != 'on' ) {
129
+ self::$stat[ 'id' ] = -204;
130
+ self::_set_error_array( __( 'Tracker is turned off, but client-side tracking code is still running.', 'wp-slimstat' ), true );
131
+ self::slimstat_save_options();
132
+ exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
133
+ }
134
+
135
+ // This function also initializes self::$data_js and removes the checksum from self::$data_js['id']
136
+ self::_check_data_integrity( self::$raw_post_array );
137
+
138
+ // Is this a request to record a new pageview?
139
+ if ( self::$data_js[ 'op' ] == 'add' || self::$data_js[ 'op' ] == 'update' ) {
140
+
141
+ // Track client-side information (screen resolution, plugins, etc)
142
+ if ( !empty( self::$data_js[ 'bw' ] ) ) {
143
+ self::$stat[ 'resolution' ] = strip_tags( trim( self::$data_js[ 'bw' ] . 'x' . self::$data_js[ 'bh' ] ) );
144
+ }
145
+ if ( !empty( self::$data_js[ 'sw' ] ) ) {
146
+ self::$stat[ 'screen_width' ] = intval( self::$data_js[ 'sw' ] );
147
+ }
148
+ if ( !empty( self::$data_js[ 'sh' ] ) ) {
149
+ self::$stat[ 'screen_height' ] = intval( self::$data_js[ 'sh' ] );
150
+ }
151
+ if ( !empty( self::$data_js[ 'pl' ] ) ) {
152
+ self::$stat[ 'plugins' ] = strip_tags( trim( self::$data_js[ 'pl' ] ) );
153
+ }
154
+ if ( !empty( self::$data_js[ 'sl' ] ) && self::$data_js[ 'sl' ] > 0 && self::$data_js[ 'sl' ] < 60000 ) {
155
+ self::$stat[ 'server_latency' ] = intval( self::$data_js[ 'sl' ] );
156
+ }
157
+ if ( !empty( self::$data_js[ 'pp' ] ) && self::$data_js[ 'pp' ] > 0 && self::$data_js[ 'pp' ] < 60000 ) {
158
+ self::$stat[ 'page_performance' ] = intval( self::$data_js[ 'pp' ] );
159
+ }
160
+ }
161
+
162
+ if ( self::$data_js[ 'op' ] == 'add' ) {
163
+ self::slimtrack();
164
+ }
165
+ else if ( self::$data_js[ 'op' ] == 'update' ) {
166
+ // Update an existing pageview with client-based information (resolution, plugins installed, etc)
167
+ self::_set_visit_id( true );
168
+
169
+ // ID of the pageview to update
170
+ self::$stat[ 'id' ] = abs( intval( self::$data_js[ 'id' ] ) );
171
+
172
+ // Visitor is still on this page, record the timestamp in the corresponding field
173
+ self::toggle_date_i18n_filters( false );
174
+ self::$stat['dt_out'] = date_i18n( 'U' );
175
+ self::toggle_date_i18n_filters( true );
176
+
177
+ // Are we tracking an outbound click?
178
+ if ( !empty( self::$data_js[ 'res' ] ) ) {
179
+ $outbound_resource = strip_tags( trim( base64_decode( self::$data_js[ 'res' ] ) ) );
180
+ $outbound_host = parse_url( $outbound_resource, PHP_URL_HOST );
181
+ $site_host = parse_url( get_site_url(), PHP_URL_HOST );
182
+ if ( $outbound_host != $site_host ) {
183
+ $existing_outbound_resource = self::$wpdb->get_var( self::$wpdb->prepare( "
184
+ SELECT outbound_resource
185
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
186
+ WHERE id = %d", self::$stat[ 'id' ]
187
+ ) );
188
+
189
+ self::$stat[ 'outbound_resource' ] = $outbound_resource;
190
+ if ( !empty( $existing_outbound_resource ) ) {
191
+ self::$stat[ 'outbound_resource' ] = $existing_outbound_resource . ';;;' .$outbound_resource;
192
+ }
193
+ }
194
+ }
195
+
196
+ self::update_row( self::$stat, $GLOBALS[ 'wpdb' ]->prefix . 'slim_stats' );
197
+ }
198
+
199
+ // Are we tracking events, coordinates and other details?
200
+ if ( self::$data_js[ 'op' ] == 'event' || !empty( self::$data_js[ 'pos' ] ) ) {
201
+ self::$stat[ 'id' ] = abs( intval( self::$data_js[ 'id' ] ) );
202
+
203
+ self::toggle_date_i18n_filters( false );
204
+ $event_info = array(
205
+ 'position' => strip_tags( trim( self::$data_js[ 'pos' ] ) ),
206
+ 'id' => self::$stat[ 'id' ],
207
+ 'dt' => date_i18n( 'U' )
208
+ );
209
+ self::toggle_date_i18n_filters( true );
210
+
211
+ if ( !empty( self::$data_js[ 'ty' ] ) ) {
212
+ $event_info[ 'type' ] = abs( intval( self::$data_js[ 'ty' ] ) );
213
+ }
214
+ if ( !empty( self::$data_js[ 'des' ] ) ) {
215
+ $event_info[ 'event_description' ] = strip_tags( trim( base64_decode( self::$data_js[ 'des' ] ) ) );
216
+ }
217
+ if ( !empty( self::$data_js[ 'no' ] ) ) {
218
+ $event_info[ 'notes' ] = strip_tags( trim( base64_decode( self::$data_js[ 'no' ] ) ) );
219
+ }
220
+
221
+ self::insert_row( $event_info, $GLOBALS[ 'wpdb' ]->prefix . 'slim_events' );
222
+ }
223
+
224
+ // Was this pageview tracked?
225
+ if ( self::$stat[ 'id' ] <= 0 ) {
226
+ $abs_error_code = abs( self::$stat[ 'id' ] );
227
+ do_action( 'slimstat_track_exit_' . $abs_error_code, self::$stat );
228
+ self::slimstat_save_options();
229
+ exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
230
+ }
231
+
232
+ // Send the ID back to Javascript to track future interactions
233
+ do_action( 'slimstat_track_success' );
234
+
235
+ // If we tracked an internal download, we return the original ID, not the new one
236
+ exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
237
+ }
238
+
239
+ /**
240
+ * Core tracking functionality
241
+ */
242
+ public static function slimtrack( $_argument = '' ) {
243
+ // If the website is using a caching plugin, the tracking code might still be there, even if the user turned off tracking
244
+ if ( self::$settings[ 'is_tracking' ] != 'on' ) {
245
+ self::$stat[ 'id' ] = -204;
246
+ self::_set_error_array( __( 'Tracker is turned off, but client-side tracking code is still running.', 'wp-slimstat' ), true );
247
+ return $_argument;
248
+ }
249
+
250
+ self::toggle_date_i18n_filters( false );
251
+ self::$stat[ 'dt' ] = date_i18n( 'U' );
252
+ self::$stat[ 'notes' ] = array();
253
+ self::toggle_date_i18n_filters( true );
254
+
255
+ // Allow third-party tools to initialize the stat array
256
+ self::$stat = apply_filters( 'slimstat_filter_pageview_stat_init', self::$stat );
257
+
258
+ // Third-party tools can decide that this pageview should not be tracked, by setting its datestamp to zero
259
+ if ( empty( self::$stat ) || empty( self::$stat[ 'dt' ] ) ) {
260
+ self::$stat[ 'id' ] = -300;
261
+ self::_set_error_array( __( 'Pageview filtered by third-party code', 'wp-slimstat' ), true );
262
+ return $_argument;
263
+ }
264
+
265
+ // User's IP address
266
+ list ( self::$stat[ 'ip' ], self::$stat[ 'other_ip' ] ) = self::_get_remote_ip();
267
+
268
+ if ( empty( self::$stat[ 'ip' ] ) || self::$stat[ 'ip' ] == '0.0.0.0' ) {
269
+ self::$stat[ 'id' ] = -202;
270
+ self::_set_error_array( __( 'Empty or not supported IP address format (IPv6)', 'wp-slimstat' ), false );
271
+ return $_argument;
272
+ }
273
+
274
+ // Referrer URL
275
+ if ( !empty( self::$data_js[ 'ref' ] ) ) {
276
+ self::$stat[ 'referer' ] = base64_decode( self::$data_js[ 'ref' ] );
277
+ }
278
+ else if ( !empty( $_SERVER[ 'HTTP_REFERER' ] ) ) {
279
+ self::$stat[ 'referer' ] = $_SERVER[ 'HTTP_REFERER' ];
280
+ }
281
+
282
+ if ( !empty( self::$stat[ 'referer' ] ) ) {
283
+
284
+ // Is this a 'seriously malformed' URL?
285
+ $referer = parse_url( self::$stat[ 'referer' ] );
286
+ if ( !$referer ) {
287
+ self::_set_error_array( sprintf( __( 'Malformed referrer URL: %s (IP: %s)', 'wp-slimstat' ), self::$stat[ 'referer' ], self::$stat[ 'ip' ] ), false, 201 );
288
+ self::$stat[ 'notes' ][] = sprintf( __( 'Malformed referrer URL: %s', 'wp-slimstat' ), self::$stat[ 'referer' ] );
289
+ unset( self::$stat[ 'referer' ] );
290
+ }
291
+
292
+ if ( !empty( $referer[ 'scheme' ] ) && !in_array( strtolower( $referer[ 'scheme' ] ), array( 'http', 'https', 'android-app' ) ) ) {
293
+ self::_set_error_array( sprintf( __( 'Attempted XSS Injection: %s (IP: %s)', 'wp-slimstat' ), self::$stat[ 'referer' ], self::$stat[ 'ip' ] ), false, 203 );
294
+ self::$stat[ 'notes' ][] = sprintf( __( 'Attempted XSS Injection: %s', 'wp-slimstat' ), self::$stat[ 'referer' ] );
295
+ unset( self::$stat[ 'referer' ] );
296
+ }
297
+
298
+ if ( !empty( self::$stat[ 'referer' ] ) ) {
299
+ $parsed_site_url_host = parse_url( get_site_url(), PHP_URL_HOST );
300
+ if ( !empty( $referer[ 'host' ] ) && $referer[ 'host' ] == $parsed_site_url_host && self::$settings[ 'track_same_domain_referers' ] != 'on' ) {
301
+ unset( self::$stat[ 'referer' ] );
302
+ }
303
+ else {
304
+ // Fix Google Images referring domain
305
+ if ( strpos( self::$stat[ 'referer' ], 'www.google' ) !== false ) {
306
+ if ( strpos( self::$stat[ 'referer' ], '/imgres?' ) !== false ) {
307
+ self::$stat[ 'referer' ] = str_replace( 'www.google', 'images.google', self::$stat[ 'referer' ] );
308
+ }
309
+ if ( strpos( self::$stat[ 'referer' ], '/url?' ) !== false ) {
310
+ self::$stat[ 'referer' ] = str_replace( '/url?', '/search?', self::$stat[ 'referer' ] );
311
+ }
312
+ }
313
+
314
+ // Is this referer blacklisted?
315
+ if ( !empty( self::$settings[ 'ignore_referers' ] ) ) {
316
+ $return_error_code = array(
317
+ -301,
318
+ sprintf( __( 'Referrer %s is blacklisted', 'wp-slimstat' ), self::$stat[ 'referer' ] ),
319
+ true
320
+ );
321
+ if ( self::_is_blacklisted( self::$stat[ 'referer' ], self::$settings[ 'ignore_referers' ], $return_error_code ) ) {
322
+ return $_argument;
323
+ }
324
+ }
325
+ }
326
+ }
327
+ }
328
+
329
+ $content_info = self::_get_content_info();
330
+
331
+ // Is this content type blacklisted?
332
+ if ( !empty( self::$settings[ 'ignore_content_types' ] ) ) {
333
+ $return_error_code = array(
334
+ -313,
335
+ sprintf( __( 'Content Type %s is blacklisted', 'wp-slimstat' ), $content_info[ 'content_type' ] ),
336
+ true
337
+ );
338
+ if ( self::_is_blacklisted( $content_info[ 'content_type' ], self::$settings[ 'ignore_content_types' ], $return_error_code ) ) {
339
+ return $_argument;
340
+ }
341
+ }
342
+
343
+ // Did we receive data from an Ajax request?
344
+ if ( !empty( self::$data_js[ 'id' ] ) ) {
345
+
346
+ // Are we tracking a new pageview? (pos is empty = no event was triggered)
347
+ if ( empty( self::$data_js[ 'pos' ] ) ) {
348
+ $content_info = unserialize( base64_decode( self::$data_js[ 'id' ] ) );
349
+ if ( $content_info === false || empty( $content_info[ 'content_type' ] ) ) {
350
+ $content_info = array();
351
+ }
352
+ }
353
+
354
+ // If pos is not empty and slimtrack was called, it means we are tracking a new internal download
355
+ else if ( !empty( self::$data_js[ 'res' ] ) ) {
356
+ $download_url = base64_decode( self::$data_js[ 'res' ] );
357
+ if ( is_string( $download_url ) ) {
358
+ if ( !empty( self::$data_js[ 'ty' ] ) && self::$data_js[ 'ty' ] == 1 ) {
359
+ unset( self::$stat[ 'id' ] );
360
+ $content_info = array( 'content_type' => 'download' );
361
+ }
362
+ }
363
+ }
364
+ }
365
+
366
+ self::$stat = self::$stat + $content_info;
367
+
368
+ // We want to record both hits and searches (performed through the site search form)
369
+ if ( is_array( self::$data_js ) && isset( self::$data_js[ 'res' ] ) ) {
370
+ $decoded_permalink = base64_decode( self::$data_js[ 'res' ] );
371
+ $parsed_permalink = parse_url( $decoded_permalink );
372
+ if ( !empty( $referer ) ) {
373
+ self::$stat[ 'searchterms' ] = self::_get_search_terms( $referer );
374
+ }
375
+
376
+ // Was this an internal search?
377
+ if ( empty( self::$stat[ 'searchterms' ] ) ) {
378
+ self::$stat[ 'searchterms' ] = self::_get_search_terms( $parsed_permalink );
379
+ }
380
+
381
+ if ( self::$stat[ 'content_type' ] == 'external' ) {
382
+ self::$stat['resource'] = $decoded_permalink;
383
+ }
384
+ else {
385
+ self::$stat['resource'] = !is_array( $parsed_permalink ) ? __( 'Malformed URL', 'wp-slimstat' ) : urldecode( $parsed_permalink[ 'path' ] ) . ( !empty( $parsed_permalink[ 'query' ] ) ? '?' . urldecode( $parsed_permalink[ 'query' ] ) : '' );
386
+ }
387
+ }
388
+ elseif ( empty( $_REQUEST[ 's' ] ) ) {
389
+ if ( !empty( $referer ) ) {
390
+ self::$stat[ 'searchterms' ] = self::_get_search_terms( $referer );
391
+ }
392
+ self::$stat[ 'resource' ] = self::get_request_uri();
393
+ }
394
+ else {
395
+ self::$stat[ 'resource' ] = parse_url( self::get_request_uri(), PHP_URL_PATH );
396
+ self::$stat[ 'searchterms' ] = str_replace( '\\', '', $_REQUEST[ 's' ] );
397
+ self::$stat[ 'referer' ] = self::get_request_uri();
398
+ if ( isset( $GLOBALS['wp_query']->found_posts ) ) {
399
+ self::$stat[ 'notes' ][] = 'results:' . intval( $GLOBALS['wp_query']->found_posts );
400
+ }
401
+ }
402
+
403
+ // Don't store empty values in the database
404
+ if ( empty( self::$stat[ 'searchterms' ] ) ) {
405
+ unset( self::$stat[ 'searchterms' ] );
406
+ }
407
+
408
+ // Do not track report pages in the admin
409
+ if ( ( !empty( self::$stat[ 'resource' ] ) && strpos( self::$stat[ 'resource' ], 'wp-admin/admin-ajax.php' ) !== false ) || ( !empty( $_GET[ 'page' ] ) && strpos( $_GET[ 'page' ], 'slimview' ) !== false ) ) {
410
+ self::$stat = array();
411
+ return $_argument;
412
+ }
413
+
414
+ // Is this resource blacklisted?
415
+ if ( !empty( self::$settings[ 'ignore_resources' ] ) ) {
416
+ $return_error_code = array(
417
+ -302,
418
+ sprintf( __( 'Permalink %s is blacklisted', 'wp-slimstat' ), self::$stat[ 'resource' ] ),
419
+ true
420
+ );
421
+ if ( self::_is_blacklisted( self::$stat[ 'resource' ], self::$settings[ 'ignore_resources' ], $return_error_code ) ) {
422
+ return $_argument;
423
+ }
424
+ }
425
+
426
+ // Should we ignore this user?
427
+ if ( !empty( $GLOBALS[ 'current_user' ]->ID ) ) {
428
+ // Don't track logged-in users, if the corresponding option is enabled
429
+ if ( self::$settings[ 'track_users' ] == 'no' ) {
430
+ self::$stat[ 'id' ] = -303;
431
+ self::_set_error_array( sprintf( __( 'Logged in user %s not tracked', 'wp-slimstat' ), $GLOBALS[ 'current_user' ]->data->user_login ), true );
432
+ return $_argument;
433
+ }
434
+
435
+ // Don't track users with given capabilities
436
+ foreach ( self::string_to_array( self::$settings[ 'ignore_capabilities' ] ) as $a_capability ) {
437
+ if ( array_key_exists( strtolower( $a_capability ), $GLOBALS[ 'current_user' ]->allcaps ) ) {
438
+ self::$stat[ 'id' ] = -304;
439
+ self::_set_error_array( sprintf( __( 'User with capability %s not tracked', 'wp-slimstat' ), $a_capability ), true );
440
+ return $_argument;
441
+ }
442
+ }
443
+
444
+ // Is this user blacklisted?
445
+ if ( !empty( self::$settings[ 'ignore_users' ] ) ) {
446
+ $return_error_code = array(
447
+ -305,
448
+ sprintf( __( 'User %s is blacklisted', 'wp-slimstat' ), $GLOBALS[ 'current_user' ]->data->user_login ),
449
+ true
450
+ );
451
+ if ( self::_is_blacklisted( $GLOBALS[ 'current_user' ]->data->user_login, self::$settings[ 'ignore_users' ], $return_error_code ) ) {
452
+ return $_argument;
453
+ }
454
+ }
455
+
456
+ self::$stat[ 'username' ] = $GLOBALS[ 'current_user' ]->data->user_login;
457
+ self::$stat[ 'notes' ][] = 'user:' . $GLOBALS[ 'current_user' ]->data->ID;
458
+ $not_spam = true;
459
+ }
460
+ elseif ( isset( $_COOKIE[ 'comment_author_' . COOKIEHASH ] ) ) {
461
+ // Is this a spammer?
462
+ $spam_comment = self::$wpdb->get_row( self::$wpdb->prepare( "
463
+ SELECT comment_author, COUNT(*) comment_count
464
+ FROM `" . DB_NAME . "`.{$GLOBALS['wpdb']->comments}
465
+ WHERE comment_author_IP = %s AND comment_approved = 'spam'
466
+ GROUP BY comment_author
467
+ LIMIT 0,1", self::$stat[ 'ip' ] ), ARRAY_A );
468
+
469
+ if ( !empty( $spam_comment[ 'comment_count' ] ) ) {
470
+ if ( self::$settings[ 'ignore_spammers' ] == 'on' ){
471
+ self::$stat[ 'id' ] = -306;
472
+ self::_set_error_array( sprintf( __( 'Spammer %s not tracked', 'wp-slimstat' ), $spam_comment[ 'comment_author' ] ), true );
473
+ return $_argument;
474
+ }
475
+ else{
476
+ self::$stat[ 'notes' ][] = 'spam:yes';
477
+ self::$stat[ 'username' ] = $spam_comment[ 'comment_author' ];
478
+ }
479
+ }
480
+ else
481
+ self::$stat[ 'username' ] = $_COOKIE[ 'comment_author_' . COOKIEHASH ];
482
+ }
483
+
484
+ // Should we ignore this IP address?
485
+ foreach ( self::string_to_array( self::$settings[ 'ignore_ip' ] ) as $a_ip_range ) {
486
+ $ip_to_ignore = $a_ip_range;
487
+
488
+ if ( strpos( $ip_to_ignore, '/' ) !== false ) {
489
+ list( $ip_to_ignore, $cidr_mask ) = explode( '/', trim( $ip_to_ignore ) );
490
+ }
491
+ else{
492
+ $cidr_mask = self::get_mask_length( $ip_to_ignore );
493
+ }
494
+
495
+ $long_masked_ip_to_ignore = substr( self::dtr_pton( $ip_to_ignore ), 0, $cidr_mask );
496
+ $long_masked_user_ip = substr( self::dtr_pton( self::$stat[ 'ip' ] ), 0, $cidr_mask );
497
+ $long_masked_user_other_ip = substr( self::dtr_pton( self::$stat[ 'other_ip' ] ), 0 , $cidr_mask );
498
+
499
+ if ( $long_masked_user_ip === $long_masked_ip_to_ignore || $long_masked_user_other_ip === $long_masked_ip_to_ignore ) {
500
+ self::$stat[ 'id' ] = -307;
501
+ self::_set_error_array( sprintf( __( 'IP address %s is blacklisted', 'wp-slimstat' ), self::$stat[ 'ip' ] . ( !empty( self::$stat[ 'other_ip' ] ) ? ' (' . self::$stat[ 'other_ip' ] . ')' : '' ) ), true );
502
+ return $_argument;
503
+ }
504
+ }
505
+
506
+ // Language
507
+ self::$stat[ 'language' ] = self::_get_language();
508
+
509
+ // Geolocation
510
+ include_once ( plugin_dir_path( __FILE__ ) . 'maxmind.php' );
511
+ $geolocation_data = maxmind_geolite2_connector::get_geolocation_info( self::$stat[ 'ip' ] );
512
+
513
+ // Invalid MaxMind data file
514
+
515
+ if ( !empty( $geolocation_data[ 'country' ][ 'iso_code' ] ) ) {
516
+
517
+ if ( $geolocation_data[ 'country' ][ 'iso_code' ] == 'xx' ) {
518
+ self::_set_error_array( __( 'Your MaxMind data file is invalid. Please uninstall it using the button in Settings > Maintenance.', 'wp-slimstat' ), false, 205 );
519
+ }
520
+ else {
521
+ self::$stat[ 'country' ] = strtolower( $geolocation_data[ 'country' ][ 'iso_code' ] );
522
+
523
+
524
+ if ( !empty( $geolocation_data[ 'city' ][ 'names' ][ 'en' ] ) ) {
525
+ self::$stat[ 'city' ] = $geolocation_data[ 'city' ][ 'names' ][ 'en' ];
526
+ }
527
+
528
+ if ( !empty( $geolocation_data[ 'subdivisions' ][ 0 ][ 'iso_code' ] ) && !empty( self::$stat[ 'city' ] ) ) {
529
+ self::$stat[ 'city' ] .= ' (' . $geolocation_data[ 'subdivisions' ][ 0 ][ 'iso_code' ] . ')';
530
+ }
531
+
532
+ if ( !empty( $geolocation_data[ 'location' ][ 'latitude' ] ) && !empty( $geolocation_data[ 'location' ][ 'longitude' ] ) ) {
533
+ self::$stat[ 'location' ] = $geolocation_data[ 'location' ][ 'latitude' ] . ',' . $geolocation_data[ 'location' ][ 'longitude' ];
534
+ }
535
+ }
536
+ }
537
+
538
+ unset( $geolocation_data );
539
+
540
+ // Anonymize IP Address?
541
+ if ( self::$settings[ 'anonymize_ip' ] == 'on' ) {
542
+ // IPv4 or IPv6
543
+ $needle = '.';
544
+ $replace = '.0';
545
+ if ( self::get_mask_length( self::$stat['ip'] ) == 128 ) {
546
+ $needle = ':';
547
+ $replace = ':0000';
548
+ }
549
+
550
+ self::$stat[ 'ip' ] = substr( self::$stat[ 'ip' ], 0, strrpos( self::$stat[ 'ip' ], $needle ) ) . $replace;
551
+
552
+ if ( !empty( self::$stat[ 'other_ip' ] ) ) {
553
+ self::$stat[ 'other_ip' ] = substr( self::$stat[ 'other_ip' ], 0, strrpos( self::$stat[ 'other_ip' ], $needle ) ) . $replace;
554
+ }
555
+ }
556
+
557
+ // Is this country blacklisted?
558
+ if ( !empty( self::$stat[ 'country' ] ) && !empty( self::$settings[ 'ignore_countries' ] ) && stripos( self::$settings[ 'ignore_countries' ], self::$stat[ 'country' ] ) !== false ) {
559
+ self::$stat['id'] = -308;
560
+ self::_set_error_array( sprintf( __('Country %s is blacklisted', 'wp-slimstat'), self::$stat[ 'country' ] ), true );
561
+ return $_argument;
562
+ }
563
+
564
+ // Mark or ignore Firefox/Safari prefetching requests (X-Moz: Prefetch and X-purpose: Preview)
565
+ if ( ( isset( $_SERVER[ 'HTTP_X_MOZ' ] ) && ( strtolower( $_SERVER[ 'HTTP_X_MOZ' ] ) == 'prefetch' ) ) ||
566
+ ( isset( $_SERVER[ 'HTTP_X_PURPOSE' ] ) && ( strtolower( $_SERVER[ 'HTTP_X_PURPOSE' ] ) == 'preview' ) ) ) {
567
+ if ( self::$settings[ 'ignore_prefetch' ] == 'on' ) {
568
+ self::$stat[ 'id' ] = -309;
569
+ self::_set_error_array( __( 'Prefetch requests are ignored', 'wp-slimstat' ), true );
570
+ return $_argument;
571
+ }
572
+ else{
573
+ self::$stat[ 'notes' ][] = 'pre:yes';
574
+ }
575
+ }
576
+
577
+ // Detect user agent
578
+ if ( empty( self::$browser ) ) {
579
+ self::$browser = slim_browser::get_browser();
580
+ }
581
+
582
+ // Are we ignoring bots?
583
+ if ( ( self::$settings[ 'javascript_mode' ] == 'on' || self::$settings[ 'ignore_bots' ] == 'on' ) && self::$browser[ 'browser_type' ] == 1 ) {
584
+ self::$stat[ 'id' ] = -310;
585
+ self::_set_error_array( __( 'Bot not tracked', 'wp-slimstat' ), true );
586
+ return $_argument;
587
+ }
588
+
589
+ // Is this browser blacklisted?
590
+ if ( !empty( self::$settings[ 'ignore_browsers' ] ) ) {
591
+ $return_error_code = array(
592
+ -311,
593
+ sprintf( __( 'Browser %s is blacklisted', 'wp-slimstat' ), self::$browser[ 'browser' ] ),
594
+ true
595
+ );
596
+ if ( self::_is_blacklisted( array( self::$browser[ 'browser' ], self::$browser[ 'user_agent' ] ), self::$settings[ 'ignore_browsers' ], $return_error_code ) ) {
597
+ return $_argument;
598
+ }
599
+ }
600
+
601
+ // Is this operating system blacklisted?
602
+ if ( !empty( self::$settings[ 'ignore_platforms' ] ) ) {
603
+ $return_error_code = array(
604
+ -312,
605
+ sprintf( __( 'Operating System %s is blacklisted', 'wp-slimstat' ), self::$browser[ 'platform' ] ),
606
+ true
607
+ );
608
+ if ( self::_is_blacklisted( self::$browser[ 'platform' ], self::$settings[ 'ignore_platforms' ], $return_error_code ) ) {
609
+ return $_argument;
610
+ }
611
+ }
612
+
613
+ self::$stat = self::$stat + self::$browser;
614
+
615
+ // This function can be called in "simulate" mode: no data will be actually saved in the database
616
+ if ( is_array( $_argument ) && isset( $_argument[ 'slimtrack_simulate' ] ) ) {
617
+ $_argument[ 'slimtrack_would_track' ] = true;
618
+ return ( $_argument );
619
+ }
620
+
621
+ // Do we need to assign a visit_id to this user?
622
+ $cookie_has_been_set = self::_set_visit_id( false );
623
+
624
+ // Allow third-party tools to modify all the data we've gathered so far
625
+ self::$stat = apply_filters( 'slimstat_filter_pageview_stat', self::$stat );
626
+ do_action( 'slimstat_track_pageview', self::$stat );
627
+
628
+ // Third-party tools can decide that this pageview should not be tracked, by setting its datestamp to zero
629
+ if (empty(self::$stat) || empty(self::$stat['dt'])){
630
+ self::$stat['id'] = -300;
631
+ self::_set_error_array( __( 'Pageview filtered by third-party code', 'wp-slimstat' ), true );
632
+ return $_argument;
633
+ }
634
+
635
+ // Implode the notes
636
+ if ( !empty( self::$stat[ 'notes' ] ) ) {
637
+ self::$stat[ 'notes' ] = implode( ';', self::$stat[ 'notes' ] );
638
+ }
639
+ else {
640
+ unset( self::$stat[ 'notes' ] );
641
+ }
642
+
643
+ // Now let's save this information in the database
644
+ self::$stat[ 'id' ] = self::insert_row( self::$stat, $GLOBALS[ 'wpdb' ]->prefix . 'slim_stats' );
645
+
646
+ // Something went wrong during the insert
647
+ if ( empty( self::$stat[ 'id' ] ) ) {
648
+
649
+ // Attempt to init the environment (plugin just activated on a blog in a MU network?)
650
+ include_once ( plugin_dir_path( __FILE__ ) . 'admin/wp-slimstat-admin.php' );
651
+ wp_slimstat_admin::init_environment( true );
652
+
653
+ // Now let's try again
654
+ self::$stat['id'] = self::insert_row(self::$stat, $GLOBALS['wpdb']->prefix.'slim_stats');
655
+
656
+ if ( empty( self::$stat[ 'id' ] ) ) {
657
+ self::$stat[ 'id' ] = -200;
658
+ self::_set_error_array( self::$wpdb->last_error, false );
659
+ return $_argument;
660
+ }
661
+ }
662
+
663
+ // Is this a new visitor?
664
+ $set_cookie = apply_filters( 'slimstat_set_visit_cookie', ( !empty( self::$settings[ 'set_tracker_cookie' ] ) && self::$settings[ 'set_tracker_cookie' ] == 'on' ) );
665
+ if ( $set_cookie ) {
666
+ if ( empty( self::$stat[ 'visit_id' ] ) && !empty( self::$stat[ 'id' ] ) ) {
667
+ // Set a cookie to track this visit (Google and other non-human engines will just ignore it)
668
+ @setcookie(
669
+ 'slimstat_tracking_code',
670
+ self::_get_id_with_checksum( self::$stat[ 'id' ] . 'id' ),
671
+ time() + 2678400, // one month
672
+ COOKIEPATH
673
+ );
674
+ }
675
+ elseif ( !$cookie_has_been_set && self::$settings[ 'extend_session' ] == 'on' && self::$stat[ 'visit_id' ] > 0 ) {
676
+ @setcookie(
677
+ 'slimstat_tracking_code',
678
+ self::_get_id_with_checksum( self::$stat[ 'visit_id' ] ),
679
+ time() + self::$settings[ 'session_duration' ],
680
+ COOKIEPATH
681
+ );
682
+ }
683
+ }
684
+
685
+ return $_argument;
686
+ }
687
+ // end slimtrack
688
+
689
+ /**
690
+ * Decodes the permalink
691
+ */
692
+ public static function get_request_uri(){
693
+ if (isset($_SERVER['REQUEST_URI'])){
694
+ return urldecode($_SERVER['REQUEST_URI']);
695
+ }
696
+ elseif (isset($_SERVER['SCRIPT_NAME'])){
697
+ return isset($_SERVER['QUERY_STRING'])?$_SERVER['SCRIPT_NAME']."?".$_SERVER['QUERY_STRING']:$_SERVER['SCRIPT_NAME'];
698
+ }
699
+ else{
700
+ return isset($_SERVER['QUERY_STRING'])?$_SERVER['PHP_SELF']."?".$_SERVER['QUERY_STRING']:$_SERVER['PHP_SELF'];
701
+ }
702
+ }
703
+ // end get_request_uri
704
+
705
+ /**
706
+ * Stores the information (array) in the appropriate table and returns the corresponding ID
707
+ */
708
+ public static function insert_row($_data = array(), $_table = ''){
709
+ if ( empty( $_data ) || empty( $_table ) ) {
710
+ return -1;
711
+ }
712
+
713
+ // Remove unwanted characters (SQL injections, anyone?)
714
+ $data_keys = array();
715
+ foreach (array_keys($_data) as $a_key){
716
+ $data_keys[] = sanitize_key($a_key);
717
+ }
718
+
719
+ self::$wpdb->query(self::$wpdb->prepare("
720
+ INSERT IGNORE INTO $_table (".implode(", ", $data_keys).')
721
+ VALUES ('.substr(str_repeat('%s,', count($_data)), 0, -1).")", $_data));
722
+
723
+ return intval(self::$wpdb->insert_id);
724
+ }
725
+ // end insert_row
726
+
727
+ /**
728
+ * Updates an existing row
729
+ */
730
+ public static function update_row($_data = array(), $_table = ''){
731
+ if (empty($_data) || empty($_table)){
732
+ return -1;
733
+ }
734
+
735
+ // Move the ID at the end of the array
736
+ $id = $_data['id'];
737
+ unset($_data['id']);
738
+
739
+ // Remove unwanted characters (SQL injections, anyone?)
740
+ $data_keys = array();
741
+ foreach (array_keys($_data) as $a_key){
742
+ $data_keys[] = sanitize_key($a_key);
743
+ }
744
+
745
+ // Add the id at the end
746
+ $_data['id'] = $id;
747
+
748
+ self::$wpdb->query(self::$wpdb->prepare("
749
+ UPDATE IGNORE $_table
750
+ SET ".implode(' = %s, ', $data_keys)." = %s
751
+ WHERE id = %d", $_data));
752
+
753
+ return 0;
754
+ }
755
+
756
+ /**
757
+ * Tries to find the user's REAL IP address
758
+ */
759
+ protected static function _get_remote_ip(){
760
+ $ip_array = array( '', '' );
761
+
762
+ if ( !empty( $_SERVER[ 'REMOTE_ADDR' ] ) && filter_var( $_SERVER[ 'REMOTE_ADDR' ], FILTER_VALIDATE_IP ) !== false ) {
763
+ $ip_array[ 0 ] = $_SERVER[ 'REMOTE_ADDR' ];
764
+ }
765
+
766
+ $originating_ip_headers = array( 'X-Forwarded-For', 'HTTP_X_FORWARDED_FOR', 'CF-Connecting-IP', 'HTTP_CLIENT_IP', 'HTTP_X_REAL_IP', 'HTTP_FORWARDED', 'HTTP_X_FORWARDED' );
767
+ foreach ( $originating_ip_headers as $a_header ) {
768
+ if ( !empty( $_SERVER[ $a_header ] ) ) {
769
+ foreach ( explode( ',', $_SERVER[ $a_header ] ) as $a_ip ) {
770
+ if ( filter_var( $a_ip, FILTER_VALIDATE_IP ) !== false && $a_ip != $ip_array[ 0 ] ) {
771
+ $ip_array[ 1 ] = $a_ip;
772
+ break;
773
+ }
774
+ }
775
+ }
776
+ }
777
+
778
+ return $ip_array;
779
+ }
780
+ // end _get_remote_ip
781
+
782
+ /**
783
+ * Extracts the accepted language from browser headers
784
+ */
785
+ protected static function _get_language(){
786
+ if(isset($_SERVER["HTTP_ACCEPT_LANGUAGE"])){
787
+
788
+ // Capture up to the first delimiter (, found in Safari)
789
+ preg_match("/([^,;]*)/", $_SERVER["HTTP_ACCEPT_LANGUAGE"], $array_languages);
790
+
791
+ // Fix some codes, the correct syntax is with minus (-) not underscore (_)
792
+ return str_replace( "_", "-", strtolower( $array_languages[0] ) );
793
+ }
794
+ return 'xx'; // Indeterminable language
795
+ }
796
+ // end _get_language
797
+
798
+ /**
799
+ * Sniffs out referrals from search engines and tries to determine the query string
800
+ */
801
+ protected static function _get_search_terms( $_url = array() ) {
802
+ if ( empty( $_url ) || !isset( $_url[ 'host' ] ) ) {
803
+ return '';
804
+ }
805
+
806
+ // Engines with different character encodings should always be listed here, regardless of their query string format
807
+ $query_formats = array(
808
+ 'baidu.com' => 'wd',
809
+ 'bing' => 'q',
810
+ 'dogpile.com' => 'q',
811
+ 'duckduckgo' => 'q',
812
+ 'eniro' => 'search_word',
813
+ 'exalead.com' => 'q',
814
+ 'excite' => 'q',
815
+ 'gigablast' => 'q',
816
+ 'google' => 'q',
817
+ 'hotbot' => 'q',
818
+ 'maktoob' => 'p',
819
+ 'mamma' => 'q',
820
+ 'naver' => 'query',
821
+ 'qwant' => 'q',
822
+ 'rambler' => 'query',
823
+ 'seznam' => 'oq',
824
+ 'soso.com' => 'query',
825
+ 'virgilio' => 'qs',
826
+ 'voila' => 'rdata',
827
+ 'yahoo' => 'p',
828
+ 'yam' => 'k',
829
+ 'yandex' => 'text',
830
+ 'yell' => 'keywords',
831
+ 'yippy' => 'query',
832
+ 'youdao' => 'q'
833
+ );
834
+
835
+ $charsets = array( 'baidu' => 'EUC-CN' );
836
+ $regex_match = implode( '|', array_keys( $query_formats ) );
837
+ $searchterms = '';
838
+
839
+ if ( !empty( $_url[ 'query' ] ) ) {
840
+ parse_str( $_url[ 'query' ], $query );
841
+ }
842
+
843
+ if ( !empty( $_url[ 'host' ] ) ) {
844
+ preg_match( "~($regex_match).~i", $_url[ 'host' ], $matches );
845
+ }
846
+
847
+ if ( !empty( $matches[ 1 ] ) ) {
848
+ // Let's remember that this is a search engine, regardless of the URL containing searchterms (thank you, NSA)
849
+ $searchterms = '_';
850
+ if ( !empty( $query[ $query_formats[ $matches[ 1 ] ] ] ) ) {
851
+ $searchterms = str_replace( '\\', '', trim( urldecode( $query[ $query_formats[ $matches[ 1 ] ] ] ) ) );
852
+ // Test for encodings different from UTF-8
853
+ if ( function_exists( 'mb_check_encoding' ) && !mb_check_encoding( $query[ $query_formats[ $matches[ 1 ] ] ], 'UTF-8' ) && !empty( $charsets[ $matches[ 1 ] ] ) ) {
854
+ $searchterms = mb_convert_encoding( urldecode( $query[ $query_formats[ $matches[ 1 ] ] ] ), 'UTF-8', $charsets[ $matches[ 1 ] ] );
855
+ }
856
+ }
857
+ }
858
+ else {
859
+ // We weren't lucky, but there's still hope
860
+ foreach( array( 'q','s','k','qt' ) as $a_format ) {
861
+ if ( !empty( $query[ $a_format ] ) ) {
862
+ $searchterms = str_replace( '\\', '', trim( urldecode( $query[ $a_format ] ) ) );
863
+ break;
864
+ }
865
+ }
866
+ }
867
+
868
+ return $searchterms;
869
+ }
870
+ // end _get_search_terms
871
+
872
+ /**
873
+ * Returns details about the resource being accessed
874
+ */
875
+ protected static function _get_content_info(){
876
+ $content_info = array( 'content_type' => 'unknown' );
877
+
878
+ // Mark 404 pages
879
+ if ( is_404() ) {
880
+ $content_info[ 'content_type' ] = '404';
881
+ }
882
+
883
+ // Type
884
+ else if ( is_single() ) {
885
+ if ( ( $post_type = get_post_type() ) != 'post' ) {
886
+ $post_type = 'cpt:' . $post_type;
887
+ }
888
+
889
+ $content_info[ 'content_type' ] = $post_type;
890
+ $content_info_array = array();
891
+ foreach ( get_object_taxonomies( $GLOBALS[ 'post' ] ) as $a_taxonomy ) {
892
+ $terms = get_the_terms( $GLOBALS[ 'post' ]->ID, $a_taxonomy );
893
+ if ( is_array( $terms ) ) {
894
+ foreach ( $terms as $a_term ) {
895
+ $content_info_array[] = $a_term->term_id;
896
+ }
897
+ $content_info[ 'category' ] = implode( ',', $content_info_array );
898
+ }
899
+ }
900
+ $content_info[ 'content_id' ] = $GLOBALS[ 'post' ]->ID;
901
+ }
902
+ else if ( is_page() ) {
903
+ $content_info[ 'content_type' ] = 'page';
904
+ $content_info[ 'content_id' ] = $GLOBALS[ 'post' ]->ID;
905
+ }
906
+ elseif (is_attachment()){
907
+ $content_info['content_type'] = 'attachment';
908
+ }
909
+ elseif (is_singular()){
910
+ $content_info['content_type'] = 'singular';
911
+ }
912
+ elseif (is_post_type_archive()){
913
+ $content_info['content_type'] = 'post_type_archive';
914
+ }
915
+ elseif (is_tag()){
916
+ $content_info['content_type'] = 'tag';
917
+ $list_tags = get_the_tags();
918
+ if (is_array($list_tags)){
919
+ $tag_info = array_pop($list_tags);
920
+ if (!empty($tag_info)) $content_info['category'] = "$tag_info->term_id";
921
+ }
922
+ }
923
+ elseif (is_tax()){
924
+ $content_info['content_type'] = 'taxonomy';
925
+ }
926
+ elseif (is_category()){
927
+ $content_info['content_type'] = 'category';
928
+ $list_categories = get_the_category();
929
+ if (is_array($list_categories)){
930
+ $cat_info = array_pop($list_categories);
931
+ if (!empty($cat_info)) $content_info['category'] = "$cat_info->term_id";
932
+ }
933
+ }
934
+ else if (is_date()){
935
+ $content_info['content_type']= 'date';
936
+ }
937
+ else if (is_author()){
938
+ $content_info['content_type'] = 'author';
939
+ }
940
+ else if ( is_archive() ) {
941
+ $content_info['content_type'] = 'archive';
942
+ }
943
+ else if ( is_search() ) {
944
+ $content_info[ 'content_type' ] = 'search';
945
+ }
946
+ else if ( is_feed() ) {
947
+ $content_info[ 'content_type' ] = 'feed';
948
+ }
949
+ else if ( is_home() || is_front_page() ) {
950
+ $content_info['content_type'] = 'home';
951
+ }
952
+ else if ( !empty( $GLOBALS[ 'pagenow' ] ) && $GLOBALS[ 'pagenow' ] == 'wp-login.php' ) {
953
+ $content_info[ 'content_type' ] = 'login';
954
+ }
955
+ else if ( !empty( $GLOBALS['pagenow'] ) && $GLOBALS['pagenow'] == 'wp-register.php' ) {
956
+ $content_info[ 'content_type' ] = 'registration';
957
+ }
958
+ // WordPress sets is_admin() to true for all ajax requests ( front-end or admin-side )
959
+ elseif ( is_admin() && ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
960
+ $content_info[ 'content_type' ] = 'admin';
961
+ }
962
+
963
+ if (is_paged()){
964
+ $content_info[ 'content_type' ] .= ',paged';
965
+ }
966
+
967
+ // Author
968
+ if ( is_singular() ) {
969
+ $author = get_the_author_meta( 'user_login', $GLOBALS[ 'post' ]->post_author );
970
+ if ( !empty( $author ) ) {
971
+ $content_info[ 'author' ] = $author;
972
+ }
973
+ }
974
+
975
+ return $content_info;
976
+ }
977
+ // end _get_content_info
978
+
979
+ /**
980
+ * Reads the cookie to get the visit_id and sets the variable accordingly
981
+ */
982
+ protected static function _set_visit_id($_force_assign = false){
983
+ $is_new_session = true;
984
+ $identifier = 0;
985
+
986
+ if ( isset( $_COOKIE[ 'slimstat_tracking_code' ] ) ) {
987
+ // Make sure only authorized information is recorded
988
+ $identifier = self::_separate_id_from_checksum( $_COOKIE[ 'slimstat_tracking_code' ] );
989
+ if ( $identifier === false ) {
990
+ return false;
991
+ }
992
+
993
+ $is_new_session = ( strpos( $identifier, 'id' ) !== false );
994
+ $identifier = intval( $identifier );
995
+ }
996
+
997
+ // User doesn't have an active session
998
+ if ( $is_new_session && ( $_force_assign || self::$settings[ 'javascript_mode' ] == 'on' ) ) {
999
+ if ( empty( self::$settings[ 'session_duration' ] ) ) {
1000
+ self::$settings[ 'session_duration' ] = 1800;
1001
+ }
1002
+
1003
+ self::$stat[ 'visit_id' ] = get_transient( 'slimstat_visit_id' );
1004
+ if ( self::$stat[ 'visit_id' ] === false ) {
1005
+ self::$stat[ 'visit_id' ] = intval( self::$wpdb->get_var( "SELECT MAX( visit_id ) FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_stats" ) );
1006
+ }
1007
+ self::$stat[ 'visit_id' ]++;
1008
+ set_transient( 'slimstat_visit_id', self::$stat[ 'visit_id' ] );
1009
+
1010
+ $set_cookie = apply_filters( 'slimstat_set_visit_cookie', ( !empty( self::$settings[ 'set_tracker_cookie' ] ) && self::$settings[ 'set_tracker_cookie' ] == 'on' ) );
1011
+ if ( $set_cookie ) {
1012
+ @setcookie(
1013
+ 'slimstat_tracking_code',
1014
+ self::_get_id_with_checksum( self::$stat[ 'visit_id' ] ),
1015
+ time() + self::$settings[ 'session_duration' ],
1016
+ COOKIEPATH
1017
+ );
1018
+ }
1019
+
1020
+ }
1021
+ elseif ( $identifier > 0 ) {
1022
+ self::$stat[ 'visit_id' ] = $identifier;
1023
+ }
1024
+
1025
+ if ( $is_new_session && $identifier > 0 ) {
1026
+ self::$wpdb->query( self::$wpdb->prepare( "
1027
+ UPDATE {$GLOBALS['wpdb']->prefix}slim_stats
1028
+ SET visit_id = %d
1029
+ WHERE id = %d AND visit_id = 0", self::$stat[ 'visit_id' ], $identifier
1030
+ ) );
1031
+ }
1032
+ return ( $is_new_session && ( $_force_assign || self::$settings[ 'javascript_mode' ] == 'on' ) );
1033
+ }
1034
+ // end _set_visit_id
1035
+
1036
+ /**
1037
+ * Makes sure that the data received from the client is well-formed (and that nobody is trying to do bad stuff)
1038
+ */
1039
+ protected static function _check_data_integrity( $_data = '' ) {
1040
+ // Parse the information we received
1041
+ self::$data_js = apply_filters( 'slimstat_filter_pageview_data_js', $_data );
1042
+
1043
+ // Do we have an id for this request?
1044
+ if ( empty( self::$data_js[ 'id' ] ) || empty( self::$data_js[ 'op' ] ) ) {
1045
+ do_action( 'slimstat_track_exit_102' );
1046
+ self::$stat[ 'id' ] = -100;
1047
+ self::_set_error_array( __( 'Invalid payload string. Try clearing your WordPress cache.', 'wp-slimstat' ), false );
1048
+ self::slimstat_save_options();
1049
+ exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
1050
+ }
1051
+
1052
+ // Make sure that the control code is valid
1053
+ self::$data_js[ 'id' ] = self::_separate_id_from_checksum( self::$data_js[ 'id' ] );
1054
+
1055
+ if ( self::$data_js['id'] === false ) {
1056
+ do_action( 'slimstat_track_exit_103' );
1057
+ self::$stat[ 'id' ] = -101;
1058
+ self::_set_error_array( __( 'Invalid data signature. Try clearing your WordPress cache.', 'wp-slimstat' ), false );
1059
+ self::slimstat_save_options();
1060
+ exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
1061
+ }
1062
+
1063
+ $intval_id = intval( self::$data_js[ 'id' ] );
1064
+ if ( $intval_id < 0 ) {
1065
+ do_action( 'slimstat_track_exit_' . abs( $intval_id ) );
1066
+ self::$stat[ 'id' ] = $intval_id;
1067
+ self::slimstat_save_options();
1068
+ exit( self::_get_id_with_checksum( self::$stat[ 'id' ] ) );
1069
+ }
1070
+ }
1071
+ // end _check_data_integrity
1072
+
1073
+ protected static function _set_error_array( $_error_message = '', $_is_notice = false, $_error_code = 0 ) {
1074
+ $error_code = empty( $_error_code ) ? abs( self::$stat[ 'id' ] ) : $_error_code;
1075
+ self::toggle_date_i18n_filters( false );
1076
+ if ( $_is_notice ) {
1077
+ self::$settings[ 'last_tracker_notice' ] = array( $error_code, $_error_message, date_i18n( 'U' ) );
1078
+ }
1079
+ else {
1080
+ self::$settings[ 'last_tracker_error' ] = array( $error_code, $_error_message, date_i18n( 'U' ) );
1081
+ }
1082
+ self::toggle_date_i18n_filters( true );
1083
+ }
1084
+
1085
+ protected static function _get_id_with_checksum( $_id = 0 ) {
1086
+ return $_id . '.' . md5( $_id . self::$settings[ 'secret' ] );
1087
+ }
1088
+
1089
+ protected static function _separate_id_from_checksum( $_id_with_checksum = '' ) {
1090
+ list( $id, $checksum ) = explode( '.', $_id_with_checksum );
1091
+
1092
+ if ( $checksum === md5( $id . self::$settings[ 'secret' ] ) ) {
1093
+ return $id;
1094
+ }
1095
+
1096
+ return false;
1097
+ }
1098
+
1099
+ protected static function _is_blacklisted( $_needles = array(), $_haystack_string = '', $_return_error_code = array( 0, '', false ) ) {
1100
+ foreach ( self::string_to_array( $_haystack_string ) as $a_item ) {
1101
+ $pattern = str_replace( array( '\*', '\!' ) , array( '(.*)', '.' ), preg_quote( $a_item, '@' ) );
1102
+
1103
+ if ( !is_array( $_needles ) ) {
1104
+ $_needles = array( $_needles );
1105
+ }
1106
+
1107
+ foreach ( $_needles as $a_needle ) {
1108
+ if ( preg_match( "@^$pattern$@i", $a_needle ) ) {
1109
+
1110
+ self::$stat[ 'id' ] = $_return_error_code[ 0 ];
1111
+ self::_set_error_array( $_return_error_code[ 1 ], $_return_error_code[ 2 ] );
1112
+ return true;
1113
+ }
1114
+ }
1115
+ }
1116
+
1117
+ return false;
1118
+ }
1119
+
1120
+ public static function is_local_ip_address( $ip_address = '' ) {
1121
+ if ( !filter_var( $ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
1122
+ return true;
1123
+ }
1124
+
1125
+ return false;
1126
+ }
1127
+
1128
+ public static function dtr_pton( $ip ){
1129
+ if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
1130
+ $unpacked = unpack( 'A4', inet_pton( $ip ) );
1131
+ }
1132
+ else if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) && defined( 'AF_INET6' ) ) {
1133
+ $unpacked = unpack( 'A16', inet_pton( $ip ) );
1134
+ }
1135
+
1136
+ $binary_ip = '';
1137
+ if ( !empty( $unpacked ) ) {
1138
+ $unpacked = str_split( $unpacked[ 1 ] );
1139
+ foreach ( $unpacked as $char ) {
1140
+ $binary_ip .= str_pad( decbin( ord( $char ) ), 8, '0', STR_PAD_LEFT );
1141
+ }
1142
+ }
1143
+
1144
+ return $binary_ip;
1145
+ }
1146
+
1147
+ public static function get_mask_length( $ip ){
1148
+ if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
1149
+ return 32;
1150
+ }
1151
+ else if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
1152
+ return 128;
1153
+ }
1154
+
1155
+ return false;
1156
+ }
1157
+
1158
+ /**
1159
+ * Opens given domains during CORS requests to admin-ajax.php
1160
+ */
1161
+ public static function open_cors_admin_ajax( $_allowed_origins = array() ) {
1162
+ $exploded_domains = self::string_to_array( self::$settings[ 'external_domains' ] );
1163
+
1164
+ if ( !empty( $exploded_domains ) && !empty( $exploded_domains[ 0 ] ) ) {
1165
+ $_allowed_origins = array_merge( $_allowed_origins, $exploded_domains );
1166
+ }
1167
+
1168
+ return $_allowed_origins;
1169
+ }
1170
+
1171
+ /**
1172
+ * Downloads the MaxMind geolocation database from their repository
1173
+ */
1174
+ public static function download_maxmind_database() {
1175
+ // Create the folder, if it doesn't exist
1176
+ if ( !file_exists( dirname( self::$maxmind_path ) ) ) {
1177
+ mkdir( dirname( self::$maxmind_path ) );
1178
+ }
1179
+
1180
+ if ( file_exists( self::$maxmind_path ) ) {
1181
+ if ( is_file( self::$maxmind_path ) ) {
1182
+ $is_deleted = @unlink( self::$maxmind_path );
1183
+ }
1184
+ else {
1185
+ // This should not happen, but hey...
1186
+ $is_deleted = @rmdir( self::$maxmind_path );
1187
+ }
1188
+
1189
+ if ( !$is_deleted ) {
1190
+ return __( "The geolocation database cannot be updated. Please check your server's file permissions and try again.", 'wp-slimstat' );
1191
+ }
1192
+ }
1193
+
1194
+ // Download the most recent database directly from MaxMind's repository
1195
+ if ( self::$settings[ 'geolocation_country' ] == 'on' ) {
1196
+ $maxmind_tmp = self::download_url( 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz' );
1197
+ }
1198
+ else {
1199
+ $maxmind_tmp = self::download_url( 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz' );
1200
+ }
1201
+
1202
+ if ( is_wp_error( $maxmind_tmp ) ) {
1203
+ return __( 'There was an error downloading the MaxMind Geolite DB:', 'wp-slimstat' ) . ' ' . $maxmind_tmp->get_error_message();
1204
+ }
1205
+
1206
+ $zh = false;
1207
+
1208
+ if ( !function_exists( 'gzopen' ) ) {
1209
+ if ( function_exists( 'gzopen64' ) ) {
1210
+ if ( false === ( $zh = gzopen64( $maxmind_tmp, 'rb' ) ) ) {
1211
+ return __( 'There was an error opening the zipped MaxMind Geolite DB.', 'wp-slimstat' );
1212
+ }
1213
+ }
1214
+ else {
1215
+ return __( 'Function gzopen not defined. Aborting.', 'wp-slimstat' );
1216
+ }
1217
+ }
1218
+ else{
1219
+ if ( false === ( $zh = gzopen( $maxmind_tmp, 'rb' ) ) ) {
1220
+ return __( 'There was an error opening the zipped MaxMind Geolite DB.', 'wp-slimstat' );
1221
+ }
1222
+ }
1223
+
1224
+ if ( false === ( $fh = fopen( self::$maxmind_path, 'wb' ) ) ) {
1225
+ return __( 'There was an error opening the MaxMind Geolite DB.', 'wp-slimstat' );
1226
+ }
1227
+
1228
+ while ( ( $data = gzread( $zh, 4096 ) ) != false ) {
1229
+ fwrite( $fh, $data );
1230
+ }
1231
+
1232
+ @gzclose( $zh );
1233
+ @fclose( $fh );
1234
+
1235
+ if ( !is_file( self::$maxmind_path ) ) {
1236
+ // Something went wrong, maybe a folder was created instead of a regular file
1237
+ @rmdir( self::$maxmind_path );
1238
+ return __( 'There was an error creating the MaxMind Geolite DB.', 'wp-slimstat' );
1239
+ }
1240
+
1241
+ @unlink( $maxmind_tmp );
1242
+
1243
+ return '';
1244
+ }
1245
+
1246
+ public static function download_url( $url ) {
1247
+ // Include the FILE API, if it's not defined
1248
+ if ( !function_exists( 'download_url' ) ) {
1249
+ require_once( ABSPATH . 'wp-admin/includes/file.php' );
1250
+ }
1251
+
1252
+ if ( !$url ) {
1253
+ return new WP_Error('http_no_url', __('Invalid URL Provided.'));
1254
+ }
1255
+
1256
+ $url_filename = basename( parse_url( $url, PHP_URL_PATH ) );
1257
+
1258
+ $tmpfname = wp_tempnam( $url_filename );
1259
+ if ( ! $tmpfname ) {
1260
+ return new WP_Error('http_no_file', __('Could not create Temporary file.'));
1261
+ }
1262
+
1263
+ $response = wp_safe_remote_get( $url, array( 'timeout' => 300, 'stream' => true, 'filename' => $tmpfname, 'user-agent' => 'Slimstat Analytics/' . self::$version . '; ' . home_url() ) );
1264
+
1265
+ if ( is_wp_error( $response ) ) {
1266
+ unlink( $tmpfname );
1267
+ return $response;
1268
+ }
1269
+
1270
+ if ( 200 != wp_remote_retrieve_response_code( $response ) ){
1271
+ unlink( $tmpfname );
1272
+ return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ) );
1273
+ }
1274
+
1275
+ return $tmpfname;
1276
+ }
1277
+
1278
+ public static function slimstat_shortcode( $_attributes = '', $_content = '' ) {
1279
+ extract( shortcode_atts( array(
1280
+ 'f' => '', // recent, popular, count, widget
1281
+ 'w' => '', // column to use (for recent, popular and count) or widget to use
1282
+ 's' => ' ', // separator
1283
+ 'o' => 0 // offset for counters
1284
+ ), $_attributes));
1285
+
1286
+ $output = $where = $as_column = '';
1287
+ $s = "<span class='slimstat-item-separator'>$s</span>";
1288
+
1289
+ // Load the localization files (for languages, operating systems, etc)
1290
+ load_plugin_textdomain( 'wp-slimstat', WP_PLUGIN_DIR .'/wp-slimstat/languages', '/wp-slimstat/languages' );
1291
+
1292
+ // Look for required fields
1293
+ if ( empty( $f ) || empty( $w ) ) {
1294
+ return '<!-- Slimstat Shortcode Error: missing parameter -->';
1295
+ }
1296
+
1297
+ // Include the Reports Library, but don't initialize the database, since we will do that separately later
1298
+ include_once( dirname(__FILE__) . '/admin/view/wp-slimstat-reports.php' );
1299
+ wp_slimstat_reports::init();
1300
+
1301
+ // Init the database library with the appropriate filters
1302
+ if ( strpos ( $_content, 'WHERE:' ) !== false ) {
1303
+ $where = html_entity_decode( str_replace( 'WHERE:', '', $_content ), ENT_QUOTES, 'UTF-8' );
1304
+ // wp_slimstat_db::init();
1305
+ }
1306
+ else{
1307
+ wp_slimstat_db::init( html_entity_decode( $_content, ENT_QUOTES, 'UTF-8' ) );
1308
+ }
1309
+
1310
+ switch( $f ) {
1311
+ case 'count':
1312
+ case 'count-all':
1313
+ $output = wp_slimstat_db::count_records( $w, $where, strpos( $f, 'all') === false ) + $o;
1314
+ break;
1315
+
1316
+ case 'widget':
1317
+ if ( empty( wp_slimstat_reports::$reports_info[ $w ] ) ) {
1318
+ return __( 'Undefined report ID', 'wp-slimstat' );
1319
+ }
1320
+
1321
+ wp_register_style( 'wp-slimstat-frontend', plugins_url( '/admin/css/slimstat.frontend.css', __FILE__ ) );
1322
+ wp_enqueue_style( 'wp-slimstat-frontend' );
1323
+
1324
+ ob_start();
1325
+ echo wp_slimstat_reports::report_header( $w );
1326
+ call_user_func( wp_slimstat_reports::$reports_info[ $w ][ 'callback' ], wp_slimstat_reports::$reports_info[ $w ][ 'callback_args' ] );
1327
+ wp_slimstat_reports::report_footer();
1328
+ $output = ob_get_contents();
1329
+ ob_end_clean();
1330
+ break;
1331
+
1332
+ case 'recent':
1333
+ case 'recent-all':
1334
+ case 'top':
1335
+ case 'top-all':
1336
+ $function = 'get_' . str_replace( '-all', '', $f );
1337
+
1338
+ if ( $w == '*' ) {
1339
+ $w = 'id';
1340
+ }
1341
+
1342
+ $w = self::string_to_array( $w );
1343
+
1344
+ // Some columns are 'special' and need be removed from the list
1345
+ $w_clean = array_diff( $w, array( 'count', 'display_name', 'hostname', 'post_link', 'post_link_no_qs', 'dt' ) );
1346
+
1347
+ // The special value 'display_name' requires the username to be retrieved
1348
+ if ( in_array( 'display_name', $w ) ) {
1349
+ $w_clean[] = 'username';
1350
+ }
1351
+
1352
+ // The special value 'post_list' requires the resource to be retrieved
1353
+ if ( in_array( 'post_link', $w ) ) {
1354
+ $w_clean[] = 'resource';
1355
+ }
1356
+
1357
+ // The special value 'post_list_no_qs' requires a substring to be calculated
1358
+ if ( in_array( 'post_link_no_qs', $w ) ) {
1359
+ $w_clean = array( 'SUBSTRING_INDEX( resource, "' . ( !get_option( 'permalink_structure' ) ? '&' : '?' ) . '", 1 )' );
1360
+ $as_column = 'resource_calculated';
1361
+ }
1362
+
1363
+ // Retrieve the data
1364
+ $results = wp_slimstat_db::$function( implode( ', ', $w_clean ), $where, '', strpos( $f, 'all' ) === false, $as_column );
1365
+
1366
+ // No data? No problem!
1367
+ if ( empty( $results ) ) {
1368
+ return '<!-- Slimstat Shortcode: No Data -->';
1369
+ }
1370
+
1371
+ // Are nice permalinks enabled?
1372
+ $permalinks_enabled = get_option( 'permalink_structure' );
1373
+
1374
+ // Format results
1375
+ $output = array();
1376
+ foreach( $results as $result_idx => $a_result ) {
1377
+ foreach( $w as $a_column ) {
1378
+ $output[ $result_idx ][ $a_column ] = "<span class='col-$a_column'>";
1379
+
1380
+ switch( $a_column ) {
1381
+ case 'count':
1382
+ $output[ $result_idx ][ $a_column ] .= $a_result[ 'counthits' ];
1383
+ break;
1384
+
1385
+ case 'country':
1386
+ $output[ $result_idx ][ $a_column ] .= __( 'c-' . $a_result[ $a_column ], 'wp-slimstat' );
1387
+ break;
1388
+
1389
+ case 'display_name':
1390
+ $user_details = get_user_by( 'login', $a_result[ 'username' ] );
1391
+ if ( !empty( $user_details ) ) {
1392
+ $output[ $result_idx ][ $a_column ] .= $user_details->display_name;
1393
+ }
1394
+ else {
1395
+ $output[ $result_idx ][ $a_column ] .= $a_result[ 'username' ];
1396
+ }
1397
+
1398
+ break;
1399
+
1400
+ case 'dt':
1401
+ $output[ $result_idx ][ $a_column ] .= date_i18n( self::$settings[ 'date_format' ] . ' ' . self::$settings[ 'time_format' ], $a_result[ 'dt' ] );
1402
+ break;
1403
+
1404
+ case 'hostname':
1405
+ $output[ $result_idx ][ $a_column ] .= gethostbyaddr( $a_result[ 'ip' ] );
1406
+ break;
1407
+
1408
+ case 'language':
1409
+ $output[ $result_idx ][ $a_column ] .= __( 'l-' . $a_result[ $a_column ], 'wp-slimstat' );
1410
+ break;
1411
+
1412
+ case 'platform':
1413
+ $output[ $result_idx ][ $a_column ] .= __( $a_result[ $a_column ], 'wp-slimstat' );
1414
+ break;
1415
+
1416
+ case 'post_link':
1417
+ case 'post_link_no_qs':
1418
+ $resource_key = ( $a_column == 'post_link' ) ? 'resource' : 'resource_calculated';
1419
+ $post_id = url_to_postid( $a_result[ $resource_key ] );
1420
+ if ( $post_id > 0 ) {
1421
+ $output[ $result_idx ][ $a_column ] .= "<a href='{$a_result[ $resource_key ]}'>" . get_the_title( $post_id ) . '</a>';
1422
+ }
1423
+ else {
1424
+ $output[ $result_idx ][ $a_column ] .= "<a href='{$a_result[ $resource_key ]}'>{$a_result[ $resource_key ]}</a>";
1425
+ }
1426
+ break;
1427
+
1428
+ default:
1429
+ $output[ $result_idx ][ $a_column ] .= $a_result[ $a_column ];
1430
+ break;
1431
+ }
1432
+ $output[ $result_idx ][ $a_column ] .= '</span>';
1433
+ }
1434
+ $output[ $result_idx ] = '<li>' . implode( $s, $output[ $result_idx ] ). '</li>';
1435
+ }
1436
+
1437
+ $output = '<ul class="slimstat-shortcode ' . $f . implode( '-', $w ). '">' . implode( '', $output ) . '</ul>';
1438
+ break;
1439
+
1440
+ default:
1441
+ break;
1442
+ }
1443
+
1444
+ return $output;
1445
+ }
1446
+
1447
+ public static function rest_api_response( $_request = array() ) {
1448
+ $filters = '';
1449
+ if ( !empty( $_request[ 'filters' ] ) ) {
1450
+ $filters = $_request[ 'filters' ];
1451
+ }
1452
+
1453
+ if ( empty( $_request[ 'dimension' ] ) ) {
1454
+ return new WP_Error( 'rest_invalid', esc_html__( 'The dimension parameter is required. Please review your request and try again.', 'wp-slimstat' ), array( 'status' => 400 ) );
1455
+ }
1456
+
1457
+ if ( empty( $_request[ 'function' ] ) ) {
1458
+ return new WP_Error( 'rest_invalid', esc_html__( 'The function parameter is required. Please review your request and try again.', 'wp-slimstat' ), array( 'status' => 400 ) );
1459
+ }
1460
+
1461
+ include_once( dirname(__FILE__) . '/admin/view/wp-slimstat-db.php' );
1462
+ wp_slimstat_db::init( $filters );
1463
+
1464
+ $response = array(
1465
+ 'function' => htmlentities( $_request[ 'function' ], ENT_QUOTES, 'UTF-8' ),
1466
+ 'dimension' => htmlentities( $_request[ 'dimension' ], ENT_QUOTES, 'UTF-8' ),
1467
+
1468
+ 'data' => 0
1469
+ );
1470
+
1471
+ switch( $_request[ 'function' ] ) {
1472
+ case 'count':
1473
+ case 'count-all':
1474
+ $response[ 'data' ] = wp_slimstat_db::count_records( $_request[ 'dimension' ], '', strpos( $_request[ 'function' ], '-all') === false );
1475
+ break;
1476
+
1477
+ case 'recent':
1478
+ case 'recent-all':
1479
+ case 'top':
1480
+ case 'top-all':
1481
+ $function = 'get_' . str_replace( '-all', '', $_request[ 'function' ] );
1482
+
1483
+ // Retrieve the data
1484
+ $response[ 'data' ] = array_values( wp_slimstat_db::$function( $_request[ 'dimension' ], '', '', strpos( $_request[ 'function' ], '-all' ) === false ) );
1485
+ break;
1486
+
1487
+ default:
1488
+ // This should never happen, because of the 'enum' condition for this parameter. But never say never...
1489
+ $response[ 'data' ] = new WP_Error( 'rest_invalid', esc_html__( 'Valid function values are count, count-all, recent, recent-all, top and top-all. Please review your request and try again.', 'wp-slimstat' ), array( 'status' => 400 ) );
1490
+ break;
1491
+ }
1492
+
1493
+ return rest_ensure_response( $response );
1494
+ }
1495
+
1496
+ public static function rest_api_authorization( $_request = array() ) {
1497
+ if ( empty( $_request[ 'token' ] ) ) {
1498
+ return new WP_Error( 'rest_invalid', esc_html__( 'Please specify a valid token in order to access this REST API.', 'wp-slimstat' ), array( 'status' => 400 ) );
1499
+ }
1500
+
1501
+ if ( !in_array( $_request[ 'token' ], self::string_to_array( self::$settings['rest_api_tokens'] ) ) ) {
1502
+ return false;
1503
+ }
1504
+
1505
+ return true;
1506
+ }
1507
+
1508
+ public static function register_rest_route() {
1509
+ register_rest_route( 'slimstat/v1', '/get', array(
1510
+ 'methods' => WP_REST_Server::READABLE,
1511
+ 'callback' => array( __CLASS__, 'rest_api_response' ),
1512
+ 'permission_callback' => array( __CLASS__, 'rest_api_authorization' ),
1513
+ 'args' => array(
1514
+ 'token' => array(
1515
+ 'description' => __( 'You will need to specify a valid token to be able to query the data. Tokens are defined in Slimstat > Settings > Access Control.', 'wp-slimstat' ),
1516
+ 'type' => 'string'
1517
+ ),
1518
+ 'function' => array(
1519
+ 'description' => __( 'This parameter specifies the type of QUERY for the dimension. Valid values are: count, count-all, recent, recent-all, top and top-all.', 'wp-slimstat' ),
1520
+ 'type' => 'string',
1521
+ 'enum' => array( 'count', 'count-all', 'recent', 'recent-all', 'top', 'top-all' )
1522
+ ),
1523
+ 'dimension' => array(
1524
+ 'description' => __( 'This parameter indicates what dimension to return: * (all data), ip, resource, browser, operating system, etc. You can only specify one dimension at a time.', 'wp-slimstat' ),
1525
+ 'type' => 'string',
1526
+ 'enum' => array( '*', 'id', 'ip', 'username', 'country', 'referer', 'resource', 'searchterms', 'browser', 'platform', 'language', 'resolution', 'content_type', 'content_id', 'outbound_resource' )
1527
+ ),
1528
+ 'filters' => array(
1529
+ 'description' => __( 'This parameter is used to filter a given dimension (resources, browsers, operating systems, etc) so that it satisfies certain conditions (i.e.: browser contains Chrome). Please make sure to urlencode this value, and to use the usual filter format: browser contains Chrome&&&referer contains slim (encoded: browser%20contains%20Chrome%26%26%26referer%20contains%20slim)', 'wp-slimstat' ),
1530
+ 'type' => 'string'
1531
+ )
1532
+ )
1533
+ ) );
1534
+ }
1535
+
1536
+ /**
1537
+ * Converts a series of comma separated values into an array
1538
+ */
1539
+ public static function string_to_array( $_option = '' ) {
1540
+ if ( empty( $_option ) || !is_string( $_option ) ) {
1541
+ return array();
1542
+ }
1543
+ else {
1544
+ return array_filter( array_map( 'trim', explode( ',', $_option ) ) );
1545
+ }
1546
+ }
1547
+
1548
+ /**
1549
+ * Toggles WordPress filters on date_i18n function
1550
+ */
1551
+ public static function toggle_date_i18n_filters( $_turn_on = true ) {
1552
+ if ( $_turn_on && !empty( self::$date_i18n_filters ) && is_array( self::$date_i18n_filters ) ) {
1553
+ foreach ( self::$date_i18n_filters as $i18n_priority => $i18n_func_list ) {
1554
+ foreach ( $i18n_func_list as $func_name => $func_args ) {
1555
+ if ( !empty( $func_args[ 'function' ] ) && is_string( $func_args[ 'function' ] ) ) {
1556
+ add_filter( 'date_i8n', $func_args[ 'function' ], $i18n_priority, intval( $func_args[ 'accepted_args' ] ) );
1557
+ }
1558
+ }
1559
+ }
1560
+ }
1561
+ else if ( !empty( $GLOBALS[ 'wp_filter' ][ 'date_i18n' ][ 'callbacks' ] ) && is_array( $GLOBALS[ 'wp_filter' ][ 'date_i18n' ][ 'callbacks' ] ) ) {
1562
+ self::$date_i18n_filters = $GLOBALS[ 'wp_filter' ][ 'date_i18n' ][ 'callbacks' ];
1563
+ remove_all_filters( 'date_i18n' );
1564
+ }
1565
+ }
1566
+
1567
+ /**
1568
+ * Imports all the 'old' options into the new array, and saves them
1569
+ */
1570
+ public static function init_options(){
1571
+ return array(
1572
+ 'version' => self::$version,
1573
+ 'secret' => wp_hash( uniqid( time(), true ) ),
1574
+ 'show_admin_notice' => 0,
1575
+ 'browscap_last_modified' => 0,
1576
+
1577
+ // General
1578
+ 'is_tracking' => 'on',
1579
+ 'javascript_mode' => 'on',
1580
+ 'enable_javascript' => 'on',
1581
+ 'track_admin_pages' => 'no',
1582
+ 'use_separate_menu' => 'on',
1583
+ 'add_posts_column' => 'no',
1584
+ 'posts_column_day_interval' => 30,
1585
+ 'posts_column_pageviews' => 'on',
1586
+ 'add_dashboard_widgets' => 'on',
1587
+ 'hide_addons' => 'no',
1588
+ 'auto_purge' => 0,
1589
+ 'auto_purge_delete' => 'on',
1590
+
1591
+ // Tracker
1592
+ 'do_not_track_outbound_classes_rel_href' => 'noslimstat,ab-item',
1593
+ 'extensions_to_track' => 'pdf,doc,xls,zip',
1594
+ 'track_same_domain_referers' => 'on',
1595
+ 'geolocation_country' => 'on',
1596
+ 'session_duration' => 1800,
1597
+ 'extend_session' => 'no',
1598
+ 'set_tracker_cookie' => 'on',
1599
+ 'enable_cdn' => 'on',
1600
+ 'ajax_relative_path' => 'no',
1601
+ 'external_domains' => '',
1602
+
1603
+ // Filters
1604
+ 'track_users' => 'on',
1605
+ 'ignore_spammers' => 'on',
1606
+ 'ignore_bots' => 'no',
1607
+ 'ignore_prefetch' => 'on',
1608
+ 'anonymize_ip' => 'no',
1609
+
1610
+ 'ignore_users' => '',
1611
+ 'ignore_ip' => '',
1612
+ 'ignore_countries' => '',
1613
+ 'ignore_browsers' => '',
1614
+ 'ignore_platforms' => '',
1615
+ 'ignore_capabilities' => '',
1616
+
1617
+ 'ignore_resources' => '',
1618
+ 'ignore_referers' => '',
1619
+ 'ignore_content_types' => '',
1620
+
1621
+ // Reports
1622
+ 'use_european_separators' => 'on',
1623
+ 'date_format' => 'm-d-y',
1624
+ 'time_format' => 'h:i a',
1625
+ 'show_display_name' => 'no',
1626
+ 'convert_resource_urls_to_titles' => 'on',
1627
+ 'convert_ip_addresses' => 'no',
1628
+ 'async_load' => 'no',
1629
+ 'use_current_month_timespan' => 'no',
1630
+ 'expand_details' => 'no',
1631
+ 'rows_to_show' => '20',
1632
+ 'limit_results' => '1000',
1633
+ 'ip_lookup_service' => 'http://www.infosniper.net/?ip_address=',
1634
+ 'mozcom_access_id' => '',
1635
+ 'mozcom_secret_key' => '',
1636
+ 'refresh_interval' => '60',
1637
+ 'number_results_raw_data' => '50',
1638
+ 'max_dots_on_map' => '50',
1639
+ 'custom_css' => '',
1640
+ 'chart_colors' => '',
1641
+ 'comparison_chart' => 'on',
1642
+ 'show_complete_user_agent_tooltip' => 'no',
1643
+ 'enable_sov' => 'no',
1644
+
1645
+ // Access Control
1646
+ 'restrict_authors_view' => 'on',
1647
+ 'capability_can_view' => 'activate_plugins',
1648
+ 'can_view' => '',
1649
+ 'rest_api_tokens' => wp_hash( uniqid( time() - 3600, true ) ),
1650
+ 'capability_can_admin' => 'activate_plugins',
1651
+ 'can_admin' => '',
1652
+
1653
+ // Maintenance
1654
+ 'last_tracker_error' => array( 0, '', 0 ),
1655
+ 'show_sql_debug' => 'no',
1656
+ 'no_maxmind_warning' => 'no',
1657
+ 'no_browscap_warning' => 'no',
1658
+
1659
+ // Network-wide Settings
1660
+ 'locked_options' => ''
1661
+ );
1662
+ }
1663
+ // end init_options
1664
+
1665
+ /**
1666
+ * Saves the options in the database, if necessary
1667
+ */
1668
+ public static function slimstat_save_options() {
1669
+ // Allow third-party functions to manipulate the options right before they are saved
1670
+ self::$settings = apply_filters( 'slimstat_save_options', self::$settings );
1671
+
1672
+ if ( self::$settings_signature === md5( serialize( self::$settings ) ) ) {
1673
+ return true;
1674
+ }
1675
+
1676
+ if ( !is_network_admin() ) {
1677
+ update_option( 'slimstat_options', self::$settings );
1678
+ }
1679
+ else {
1680
+ update_site_option( 'slimstat_options', self::$settings );
1681
+ }
1682
+
1683
+ return true;
1684
+ }
1685
+
1686
+ /**
1687
+ * Enqueue a javascript to track users' screen resolution and other browser-based information
1688
+ */
1689
+ public static function wp_slimstat_enqueue_tracking_script() {
1690
+ // Do not enqueue the script if the corresponding options are turned off
1691
+ $is_tracking_filter_js = apply_filters( 'slimstat_filter_pre_tracking_js', true );
1692
+ if ( ( self::$settings[ 'enable_javascript' ] != 'on' && self::$settings[ 'javascript_mode' ] != 'on' ) || self::$settings[ 'is_tracking' ] != 'on' || !$is_tracking_filter_js ) {
1693
+ return 0;
1694
+ }
1695
+
1696
+ // Register the script in WordPress
1697
+ $jstracker_suffix = ( defined( 'SCRIPT_DEBUG' ) && is_bool( SCRIPT_DEBUG ) && SCRIPT_DEBUG ) ? '' : '.min';
1698
+
1699
+ // Pass some information to the tracker
1700
+ $params = array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) );
1701
+
1702
+ if ( self::$settings[ 'ajax_relative_path' ] == 'on' ) {
1703
+ $params[ 'ajaxurl' ] = admin_url( 'admin-ajax.php', 'relative' );
1704
+ }
1705
+
1706
+ if ( !empty( self::$settings[ 'extensions_to_track' ] ) ) {
1707
+ $params[ 'extensions_to_track' ] = str_replace( ' ', '', self::$settings[ 'extensions_to_track' ] );
1708
+ }
1709
+ if ( !empty( self::$settings[ 'do_not_track_outbound_classes_rel_href' ] ) ) {
1710
+ $params[ 'outbound_classes_rel_href_to_not_track' ] = str_replace( ' ', '', self::$settings[ 'do_not_track_outbound_classes_rel_href' ] );
1711
+ }
1712
+
1713
+ if ( self::$settings[ 'javascript_mode' ] != 'on' ) {
1714
+ // Do not enqueue the tracker if this page view was not tracked for some reason
1715
+ if ( intval( self::$stat[ 'id' ] ) < 0 ) {
1716
+ return false;
1717
+ }
1718
+
1719
+ if ( !empty( self::$stat[ 'id' ] ) ) {
1720
+ $params[ 'id' ] = self::_get_id_with_checksum( self::$stat[ 'id' ] );
1721
+ }
1722
+ else {
1723
+ $params[ 'id' ] = self::_get_id_with_checksum( '-300' );
1724
+ }
1725
+ }
1726
+ else {
1727
+ // Check filters and blacklists to see if this page should be tracked
1728
+ $simulate = self::slimtrack( array( 'slimtrack_simulate' => true ) );
1729
+ if ( empty( $simulate[ 'slimtrack_would_track' ] ) ) {
1730
+ return false;
1731
+ }
1732
+
1733
+ $encoded_ci = base64_encode( serialize( self::_get_content_info() ) );
1734
+ $params[ 'ci' ] = self::_get_id_with_checksum( $encoded_ci );
1735
+ }
1736
+
1737
+ $params = apply_filters( 'slimstat_js_params', $params );
1738
+
1739
+ if ( self::$settings[ 'enable_cdn' ] == 'on' ) {
1740
+ $schema = is_ssl() ? 'https' : 'http';
1741
+ wp_register_script( 'wp_slimstat', $schema . '://cdn.jsdelivr.net/wp/wp-slimstat/tags/' . self::$version . '/wp-slimstat.min.js', array(), null, true );
1742
+ }
1743
+ else{
1744
+ wp_register_script( 'wp_slimstat', plugins_url( "/wp-slimstat{$jstracker_suffix}.js", __FILE__ ), array(), null, true );
1745
+ }
1746
+
1747
+ wp_enqueue_script( 'wp_slimstat' );
1748
+ wp_localize_script( 'wp_slimstat', 'SlimStatParams', $params );
1749
+ }
1750
+
1751
+ /**
1752
+ * Removes old entries from the main table and performs other daily tasks
1753
+ */
1754
+ public static function wp_slimstat_purge() {
1755
+ $autopurge_interval = intval( self::$settings[ 'auto_purge' ] );
1756
+
1757
+ if ( $autopurge_interval <= 0 ) {
1758
+ return;
1759
+ }
1760
+
1761
+ self::toggle_date_i18n_filters( false );
1762
+ $days_ago = strtotime( date_i18n( 'Y-m-d H:i:s' ) . " -$autopurge_interval days" );
1763
+ self::toggle_date_i18n_filters( true );
1764
+
1765
+ // Copy entries to the archive table, if needed
1766
+ if ( self::$settings[ 'auto_purge_delete' ] != 'no' ) {
1767
+ $is_copy_done = self::$wpdb->query("
1768
+ INSERT INTO {$GLOBALS['wpdb']->prefix}slim_stats_archive (id, ip, other_ip, username, country, location, city, referer, resource, searchterms, plugins, notes, visit_id, server_latency, page_performance, browser, browser_version, browser_type, platform, language, user_agent, resolution, screen_width, screen_height, content_type, category, author, content_id, outbound_resource, dt_out, dt)
1769
+ SELECT id, ip, other_ip, username, country, location, city, referer, resource, searchterms, plugins, notes, visit_id, server_latency, page_performance, browser, browser_version, browser_type, platform, language, user_agent, resolution, screen_width, screen_height, content_type, category, author, content_id, outbound_resource, dt_out, dt
1770
+ FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_stats
1771
+ WHERE dt < $days_ago");
1772
+
1773
+ if ( $is_copy_done !== false ) {
1774
+ self::$wpdb->query("DELETE ts FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_stats ts WHERE ts.dt < $days_ago");
1775
+ }
1776
+
1777
+ $is_copy_done = self::$wpdb->query("
1778
+ INSERT INTO {$GLOBALS['wpdb']->prefix}slim_events_archive (type, event_description, notes, position, id, dt)
1779
+ SELECT type, event_description, notes, position, id, dt
1780
+ FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_events
1781
+ WHERE dt < $days_ago");
1782
+
1783
+ if ( $is_copy_done !== false ) {
1784
+ self::$wpdb->query("DELETE te FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_events te WHERE te.dt < $days_ago");
1785
+ }
1786
+ }
1787
+ else {
1788
+ // Delete old entries
1789
+ self::$wpdb->query("DELETE ts FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_stats ts WHERE ts.dt < $days_ago");
1790
+ self::$wpdb->query("DELETE te FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_events te WHERE te.dt < $days_ago");
1791
+ }
1792
+
1793
+ // Optimize tables
1794
+ self::$wpdb->query( "OPTIMIZE TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_stats" );
1795
+ self::$wpdb->query( "OPTIMIZE TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_stats_archive" );
1796
+ self::$wpdb->query( "OPTIMIZE TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_events" );
1797
+ self::$wpdb->query( "OPTIMIZE TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_events_archive" );
1798
+ }
1799
+
1800
+ /**
1801
+ * Checks for add-on updates
1802
+ */
1803
+ public static function update_checker() {
1804
+ if ( empty( self::$update_checker ) || !is_admin() ) {
1805
+ return true;
1806
+ }
1807
+
1808
+ $update_checker_objects = array();
1809
+
1810
+ // This is only included if add-ons are installed
1811
+ include_once( plugin_dir_path( __FILE__ ) . 'admin/update-checker/plugin-update-checker.php' );
1812
+
1813
+ foreach ( self::$update_checker as $a_slug ) {
1814
+ $a_clean_slug = str_replace( array( 'wp_slimstat_', '_' ), array( '', '-' ), $a_slug );
1815
+
1816
+ if ( !empty( self::$settings[ 'addon_licenses' ][ 'wp-slimstat-' . $a_clean_slug ] ) ) {
1817
+ $update_checker_objects[ $a_clean_slug ] = Puc_v4_Factory::buildUpdateChecker( 'http://www.wp-slimstat.com/update-checker/?slug=' . $a_clean_slug . '&key=' . urlencode( self::$settings[ 'addon_licenses' ][ 'wp-slimstat-' . $a_clean_slug ] ), dirname( dirname( __FILE__ ) ) . '/wp-slimstat-' . $a_clean_slug . '/index.php', 'wp-slimstat-' . $a_clean_slug );
1818
+
1819
+ add_filter( "plugin_action_links_wp-slimstat-{$a_clean_slug}/index.php", array( __CLASS__, 'add_plugin_manual_download_link' ), 10, 2 );
1820
+ }
1821
+ }
1822
+ }
1823
+
1824
+ public static function add_plugin_manual_download_link( $_links = array(), $_plugin_file = '' ) {
1825
+ $a_clean_slug = str_replace( array( 'wp-slimstat-', '/index.php' ), array( '', '' ), $_plugin_file );
1826
+
1827
+ if ( false !== ( $download_url = get_transient( 'wp-slimstat-download-link-' . $a_clean_slug ) ) ) {
1828
+ $_links[] = '<a href="' . $download_url . '">Download ZIP</a>';
1829
+ }
1830
+ else {
1831
+ $url = 'http://www.wp-slimstat.com/update-checker/?slug=' . $a_clean_slug . '&key=' . urlencode( self::$settings[ 'addon_licenses' ][ 'wp-slimstat-' . $a_clean_slug ] );
1832
+ $response = wp_safe_remote_get( $url, array( 'timeout' => 300, 'user-agent' => 'Slimstat Analytics/' . self::$version . '; ' . home_url() ) );
1833
+
1834
+ if ( !is_wp_error( $response ) && 200 == wp_remote_retrieve_response_code( $response ) ) {
1835
+ $data = @json_decode( $response[ 'body' ] );
1836
+
1837
+ if ( is_object( $data ) ) {
1838
+ $_links[] = '<a href="' . $data->download_url . '">Download ZIP</a>';
1839
+ set_transient( 'wp-slimstat-download-link-' . $a_clean_slug, $data->download_url, 172800 ); // 48 hours
1840
+ }
1841
+ }
1842
+ }
1843
+
1844
+ return $_links;
1845
+ }
1846
+
1847
+ public static function register_widget() {
1848
+ return register_widget( "slimstat_widget" );
1849
+ }
1850
+ }
1851
+ // end of class declaration
1852
+
1853
+ class slimstat_widget extends WP_Widget {
1854
+
1855
+ /**
1856
+ * Sets up the widgets name etc
1857
+ */
1858
+ public function __construct() {
1859
+ parent::__construct( 'slimstat_widget', 'SlimStat', array(
1860
+ 'classname' => 'slimstat_widget',
1861
+ 'description' => 'Add a SlimStat report to your sidebar',
1862
+ ) );
1863
+ }
1864
+
1865
+ /**
1866
+ * Outputs the content of the widget
1867
+ *
1868
+ * @param array $args
1869
+ * @param array $instance
1870
+ */
1871
+ public function widget( $args, $instance ) {
1872
+ extract( $instance );
1873
+
1874
+ $slimstat_widget_filters = empty( $slimstat_widget_filters ) ? '' : $slimstat_widget_filters;
1875
+
1876
+ if ( !empty( $slimstat_widget_id ) ) {
1877
+ echo do_shortcode( "[slimstat f='widget' w='{$slimstat_widget_id}']{$slimstat_widget_filters}[/slimstat]" );
1878
+ }
1879
+ else {
1880
+ echo '';
1881
+ }
1882
+ }
1883
+
1884
+ /**
1885
+ * Outputs the options form on admin
1886
+ *
1887
+ * @param array $instance The widget options
1888
+ */
1889
+ public function form( $instance ) {
1890
+ // Let's build the dropdown
1891
+ include_once( dirname(__FILE__) . '/admin/view/wp-slimstat-reports.php' );
1892
+ wp_slimstat_reports::init();
1893
+ $select_options = '';
1894
+ $slimstat_widget_id = !empty( $instance[ 'slimstat_widget_id' ] ) ? $instance[ 'slimstat_widget_id' ] : '';
1895
+ $slimstat_widget_filters = !empty( $instance[ 'slimstat_widget_filters' ] ) ? $instance[ 'slimstat_widget_filters' ] : '';
1896
+
1897
+ foreach ( wp_slimstat_reports::$reports_info as $a_report_id => $a_report_info ) {
1898
+ $select_options .= "<option value='$a_report_id' " . ( ( $slimstat_widget_id == $a_report_id ) ? 'selected="selected"' : '' ) . ">{$a_report_info[ 'title' ]}</option>";
1899
+ }
1900
+ ?>
1901
+
1902
+ <p>
1903
+ <label for="<?php echo esc_attr( $this->get_field_id( 'slimstat_widget_id' ) ); ?>">Widget</label>
1904
+ <select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'slimstat_widget_id' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'slimstat_widget_id' ) ); ?>">
1905
+ <option value="">Select a widget</option>
1906
+ <?php echo $select_options ?>
1907
+ </select>
1908
+ </p>
1909
+
1910
+ <p>
1911
+ <label for="<?php echo esc_attr( $this->get_field_id( 'slimstat_widget_filters' ) ); ?>"><?php _e( 'Optional filters', 'wp-slimstat' ); ?></label>
1912
+ <a href="https://slimstat.freshdesk.com/solution/articles/5000631833-what-is-the-syntax-of-a-slimstat-shortcode-#slimstat-operators" target="_blank">[?]</a>
1913
+ <textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'slimstat_widget_filters' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'slimstat_widget_filters' ) ); ?>"><?php echo trim( strip_tags( $slimstat_widget_filters ) ) ?></textarea>
1914
+ </p>
1915
+ <?php
1916
+ }
1917
+
1918
+ /**
1919
+ * Processing widget options on save
1920
+ *
1921
+ * @param array $new_instance The new options
1922
+ * @param array $old_instance The previous options
1923
+ */
1924
+ public function update( $new_instance, $old_instance ) {
1925
+ $instance = $old_instance;
1926
+
1927
+ $instance[ 'slimstat_widget_id' ] = $new_instance[ 'slimstat_widget_id' ];
1928
+ $instance[ 'slimstat_widget_filters' ] = $new_instance[ 'slimstat_widget_filters' ];
1929
+ return $instance;
1930
+ }
1931
+ }
1932
+
1933
+ // Ok, let's go, Sparky!
1934
+ if ( function_exists( 'add_action' ) ) {
1935
+ // Since we use sendBeacon, this function sends raw POST data, which does not populate the $_POST variable automatically
1936
+ if ( ( !empty( $_SERVER[ 'HTTP_CONTENT_TYPE' ] ) || !empty( $_SERVER[ 'CONTENT_TYPE' ] ) ) && empty( $_POST ) ) {
1937
+ $raw_post_string = file_get_contents( 'php://input' );
1938
+ parse_str( $raw_post_string, wp_slimstat::$raw_post_array );
1939
+ }
1940
+ else if ( !empty( $_POST ) ) {
1941
+ wp_slimstat::$raw_post_array = $_POST;
1942
+ }
1943
+
1944
+ // Init the Ajax listener
1945
+ if ( !empty( wp_slimstat::$raw_post_array[ 'action' ] ) && wp_slimstat::$raw_post_array[ 'action' ] == 'slimtrack' ) {
1946
+
1947
+ // This is needed because admin-ajax.php is reading $_REQUEST to fire the corresponding action
1948
+ if ( empty( $_POST[ 'action' ] ) ) {
1949
+ $_POST[ 'action' ] = wp_slimstat::$raw_post_array[ 'action' ];
1950
+ }
1951
+
1952
+ add_action( 'wp_ajax_nopriv_slimtrack', array( 'wp_slimstat', 'slimtrack_ajax' ) );
1953
+ add_action( 'wp_ajax_slimtrack', array( 'wp_slimstat', 'slimtrack_ajax' ) );
1954
+ }
1955
+
1956
+ // From the codex: You can't call register_activation_hook() inside a function hooked to the 'plugins_loaded' or 'init' hooks (or any other hook). These hooks are called before the plugin is loaded or activated.
1957
+ if ( is_admin() ) {
1958
+ include_once( WP_PLUGIN_DIR . '/wp-slimstat/admin/wp-slimstat-admin.php' );
1959
+ register_activation_hook( __FILE__, array( 'wp_slimstat_admin', 'init_environment' ) );
1960
+ register_deactivation_hook( __FILE__, array( 'wp_slimstat_admin', 'deactivate' ) );
1961
+ }
1962
+
1963
+ add_action( 'widgets_init', array( 'wp_slimstat', 'register_widget' ) );
1964
+
1965
+ // Add the appropriate actions
1966
+ add_action( 'plugins_loaded', array( 'wp_slimstat', 'init' ), 20 );
1967
+ }