PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
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 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-stats / src / class-main.php

class-main.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at jetpack_vendor/automattic/jetpack-stats/src/class-main.php

280 lines 7.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 }
280