PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-stats/src/class-main.php +279 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,279 @@
1 +<?php
2 +/**
3 + * Stats Main
4 + *
5 + * @package automattic/jetpack-stats
6 + */
7 +
8 +namespace Automattic\Jetpack\Stats;
9 +
10 +use Automattic\Jetpack\Connection\Manager as Connection_Manager;
11 +use Automattic\Jetpack\Constants;
12 +use Automattic\Jetpack\IP\Utils as IP_Utils;
13 +use Automattic\Jetpack\Modules;
14 +use Automattic\Jetpack\Stats\Abilities\Stats_Abilities;
15 +use Automattic\Jetpack\Status;
16 +use Automattic\Jetpack\Status\Visitor;
17 +use WP_User;
18 +
19 +/**
20 + * Stats Main class.
21 + *
22 + * Entrypoint for Stats.
23 + *
24 + * @since 0.1.0
25 + */
26 +class Main {
27 + /**
28 + * Stats version.
29 + * Mostly needed for backwards compatibility.
30 + */
31 + const STATS_VERSION = '9';
32 +
33 + /**
34 + * Singleton Main instance.
35 + *
36 + * @var Main
37 + **/
38 + private static $instance = null;
39 +
40 + /**
41 + * Initializer.
42 + * Used to configure the stats package, eg when called via the Config package.
43 + *
44 + * @return object
45 + */
46 + public static function init() {
47 + if ( null === self::$instance ) {
48 + self::$instance = new Main();
49 + }
50 +
51 + return self::$instance;
52 + }
53 +
54 + /**
55 + * Class constructor.
56 + *
57 + * @return void
58 + */
59 + private function __construct() {
60 + /**
61 + * This avoids conflicts when running Stats package with older versions of the Jetpack plugin.
62 + *
63 + * On JP version 11.5-a.2 the hooks below were removed from the Jetpack plugin and it is safe
64 + * to register them in the Stats package.
65 + */
66 + $jp_plugin_version = Constants::get_constant( 'JETPACK__VERSION' );
67 + if ( $jp_plugin_version && version_compare( $jp_plugin_version, '11.5-a.2', '<' ) ) {
68 + return;
69 + }
70 + // Generate the tracking code after wp() has queried for posts.
71 + add_action( 'template_redirect', array( __CLASS__, 'template_redirect' ), 1 );
72 +
73 + add_action( 'wp_enqueue_scripts', array( __CLASS__, 'hide_smile_css' ) );
74 +
75 + // Map stats caps.
76 + add_filter( 'map_meta_cap', array( __CLASS__, 'map_meta_caps' ), 10, 3 );
77 +
78 + XMLRPC_Provider::init();
79 +
80 + /*
81 + * REST_Provider only registers its routes on REST init, so defer
82 + * constructing it (and autoloading the class) until a REST request is
83 + * served. A closure is used because rest_api_init passes the REST server
84 + * to callbacks, which would otherwise be read as REST_Provider::init()'s
85 + * $new_instance argument.
86 + */
87 + add_action(
88 + 'rest_api_init',
89 + static function () {
90 + REST_Provider::init();
91 + },
92 + 0
93 + );
94 + Transient_Cleanup::init();
95 +
96 + // Clean up transient cron on module deactivation.
97 + add_action( 'jetpack_deactivate_module_stats', array( Transient_Cleanup::class, 'unschedule_cleanup' ) );
98 +
99 + // Set up package version hook.
100 + add_filter( 'jetpack_package_versions', __NAMESPACE__ . '\Package_Version::send_package_version_to_tracker' );
101 +
102 + // Register WP Abilities API surface. Gated behind the
103 + // `jetpack_wp_abilities_enabled` filter inside Registrar::init(),
104 + // which defaults to false — so this call is safe to make unconditionally
105 + // and still opt-in per-site until the flag is flipped.
106 + Stats_Abilities::init();
107 + }
108 +
109 + /**
110 + * Checks if filter is set and dnt is enabled.
111 + *
112 + * @return bool
113 + */
114 + public static function jetpack_is_dnt_enabled() {
115 + /**
116 + * Filter the option which decides honor DNT or not.
117 + *
118 + * @module stats
119 + * @since-jetpack 6.1.0
120 + *
121 + * @param bool false Honors DNT for clients who don't want to be tracked. Defaults to false. Set to true to enable.
122 + */
123 + if ( false === apply_filters( 'jetpack_honor_dnt_header_for_stats', false ) ) {
124 + return false;
125 + }
126 +
127 + foreach ( $_SERVER as $name => $value ) {
128 + if ( 'http_dnt' === strtolower( $name ) && 1 === (int) $value ) {
129 + return true;
130 + }
131 + }
132 +
133 + return false;
134 + }
135 +
136 + /**
137 + * Maps view_stats cap to read cap as needed.
138 + *
139 + * @access public
140 + * @param mixed $caps Caps.
141 + * @param mixed $cap Cap.
142 + * @param mixed $user_id User ID.
143 + * @return array Possibly mapped capabilities for meta capability.
144 + */
145 + public static function map_meta_caps( $caps, $cap, $user_id ) {
146 + // Map view_stats to exists.
147 + if ( 'view_stats' === $cap ) {
148 + $user = new WP_User( $user_id );
149 + $stats_roles = Options::get_option( 'roles' );
150 +
151 + // Is any of the user's roles in the available stats roles?
152 + if ( is_array( $stats_roles ) && ! empty( array_intersect( $user->roles, $stats_roles ) ) ) {
153 + $caps = array( 'read' );
154 + }
155 + }
156 +
157 + return $caps;
158 + }
159 +
160 + /**
161 + * Stats Template Redirect.
162 + *
163 + * @access public
164 + * @return void
165 + */
166 + public static function template_redirect() {
167 + if ( ! self::should_track() ) {
168 + return;
169 + }
170 +
171 + add_action( 'wp_enqueue_scripts', array( Tracking_Pixel::class, 'enqueue_stats_script' ), 101 );
172 + add_action( 'wp_footer', array( Tracking_Pixel::class, 'add_amp_pixel' ), 101 );
173 + add_action( 'web_stories_print_analytics', array( Tracking_Pixel::class, 'add_amp_pixel' ), 101 );
174 + }
175 +
176 + /**
177 + * CSS to hide the tracking pixel smiley.
178 + * It is now hidden for everyone (used to be visible if you had set the hide_smile option).
179 + *
180 + * @access public
181 + * @return void
182 + */
183 + public static function hide_smile_css() {
184 + if ( ! self::should_track() ) {
185 + return;
186 + }
187 +
188 + wp_register_style( 'jetpack-stats', false, array(), Package_Version::PACKAGE_VERSION );
189 + wp_enqueue_style( 'jetpack-stats' );
190 + wp_add_inline_style( 'jetpack-stats', 'img#wpstats{display:none}' );
191 + }
192 +
193 + /**
194 + * Whether we should add the tracking pixel.
195 + *
196 + * @return bool
197 + */
198 + public static function should_track() {
199 + global $current_user;
200 +
201 + // Not connected sites should not generate tracking stats.
202 + if ( ! ( new Connection_Manager() )->is_connected() ) {
203 + return false;
204 + }
205 +
206 + // If the stats module is disabled we should not generate tracking stats.
207 + if ( ! ( new Modules() )->is_active( 'stats' ) ) {
208 + return false;
209 + }
210 +
211 + // Do not generate tracking stats for feeds, robots, embeds, previews
212 + // or to honour the DNT headers.
213 + if (
214 + is_feed()
215 + || is_robots()
216 + || is_embed()
217 + || is_trackback()
218 + || is_preview()
219 + || self::jetpack_is_dnt_enabled()
220 + ) {
221 + return false;
222 + }
223 +
224 + // Sites in Safe Mode should not generate tracking stats.
225 + $status = new Status();
226 + if ( $status->in_safe_mode() ) {
227 + return false;
228 + }
229 +
230 + // Should we be counting this user's views?
231 + if ( ! empty( $current_user->ID ) ) {
232 + $count_roles = Options::get_option( 'count_roles' );
233 + if ( ! is_array( $count_roles ) || ! array_intersect( $current_user->roles, $count_roles ) ) {
234 + return false;
235 + }
236 + }
237 +
238 + /**
239 + * Allow excluding specific IP addresses from being tracked in Stats.
240 + * Note: for this to work well, visitors' IP addresses must:
241 + * - be stored and returned properly in IP address headers;
242 + * - not be impacted by any caching setup on your site.
243 + *
244 + * @module stats
245 + *
246 + * @since-jetpack 10.6
247 + *
248 + * @param array $excluded_ips An array of IP address strings to exclude from tracking.
249 + */
250 + $excluded_ips = (array) apply_filters( 'jetpack_stats_excluded_ips', array() );
251 +
252 + /*
253 + * Visitor::get_ip() returns a normalized address, so normalize the configured list the
254 + * same way before comparing. Without this an entry written as `::ffff:203.0.113.5` or
255 + * with uppercase IPv6 hex would never match, and the site owner's traffic would be
256 + * counted with no indication why. Non-strings are dropped: they could never match the
257 + * string get_ip() returns under the strict comparison below.
258 + */
259 + $excluded_ips = array_filter(
260 + array_map( array( IP_Utils::class, 'clean_ip' ), array_filter( $excluded_ips, 'is_string' ) )
261 + );
262 +
263 + /*
264 + * Resolving the visitor address reads request headers and, on a site with brute force
265 + * protection configured, a site option, so only do it when the normalized list still
266 + * holds something to compare against. The filter is unset on almost every site, which
267 + * makes this the common path.
268 + */
269 + if ( ! empty( $excluded_ips ) ) {
270 + // Should we be counting views for this IP address?
271 + $current_user_ip = ( new Visitor() )->get_ip( true );
272 + if ( in_array( $current_user_ip, $excluded_ips, true ) ) {
273 + return false;
274 + }
275 + }
276 +
277 + return true;
278 + }
279 +}