Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/seo-by-rank-math/includes/class-tracking.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 + /**
3 + * Code related to the Mixpanel Tracking.
4 + *
5 + * @since 1.0.x
6 + * @package RankMath
7 + * @subpackage RankMath\Core
8 + * @author Rank Math <support@rankmath.com>
9 + */
10 +
11 + declare(strict_types=1);
12 +
13 + namespace RankMath;
14 +
15 + use RankMath\Traits\Hooker;
16 + use RankMath\Helper;
17 + use RankMath\Helpers\Param;
18 + use RankMath\Helpers\Str;
19 + use RankMath\Helpers\Editor;
20 + use RankMath\Admin\Admin_Helper;
21 + use WPMedia\Mixpanel\Optin;
22 + use WPMedia\Mixpanel\TrackingPlugin;
23 +
24 + /**
25 + * Tracking class.
26 + */
27 + class Tracking {
28 + use Hooker;
29 +
30 + /**
31 + * Opt-in instance.
32 + *
33 + * @var Optin
34 + */
35 + private $optin;
36 +
37 + /**
38 + * Mixpanel instance.
39 + *
40 + * @var TrackingPlugin
41 + */
42 + private $mixpanel;
43 +
44 + /**
45 + * User email for identification.
46 + *
47 + * @var string
48 + */
49 + private $user_email = '';
50 +
51 + /**
52 + * User language.
53 + *
54 + * @var string
55 + */
56 + private $user_language = '';
57 +
58 + /**
59 + * Plugin name.
60 + *
61 + * @var string
62 + */
63 + private $plugin = '';
64 +
65 + /**
66 + * Constructor.
67 + */
68 + public function __construct() {
69 + $this->plugin = defined( 'RANK_MATH_PRO_VERSION' ) ? 'Rank Math Pro ' . RANK_MATH_PRO_VERSION : 'Rank Math Free ' . rank_math()->version;
70 + $this->optin = new Optin( 'rank_math', 'manage_options' );
71 + $this->mixpanel = new TrackingPlugin( '517e881edc2636e99a2ecf013d8134d3', $this->plugin, 'RankMath', 'RankMath' );
72 +
73 + $this->action( 'init', 'hooks' );
74 + }
75 +
76 + /**
77 + * Initialize the tracking class hooks.
78 + */
79 + public function hooks(): void {
80 + $this->init_user_data();
81 + $this->action( 'rank_math_mixpanel_optin_changed', 'track_optin_change' );
82 + $this->action( 'rank_math/module_changed', 'track_module_option_change', 10, 2 );
83 + $this->action( 'rank_math/setup_wizard/enable_tracking', 'enable_usage_tracking' );
84 + $this->action( 'rank_math/setup_wizard/step_viewed', 'track_setup_wizard_step_view' );
85 + $this->action( 'rank_math/admin/enqueue_scripts', 'enqueue_mixpanel' );
86 + $this->action( 'admin_init', 'track_admin_page_view' );
87 + $this->action( 'rank_math/admin/options/general_data', 'set_usage_tracking_option', 99 );
88 + $this->action( 'rank_math/settings/before_save', 'update_mixpanel_optin', 10, 2 );
89 + $this->filter( 'rank_math/settings/saved_data', 'add_mixpanel_data', 10, 2 );
90 + $this->action( 'cmb2_save_options-page_fields_rank-math-options-general_options', 'update_mixpanel_optin_cmb2' );
91 + }
92 +
93 + /**
94 + * Track the opt-in status change.
95 + *
96 + * @param bool $status The new opt-in status.
97 + */
98 + public function track_optin_change( $status ): void {
99 + $this->identify_user();
100 + $this->mixpanel->track_optin( $status );
101 + }
102 +
103 + /**
104 + * Track module option changes.
105 + *
106 + * @param string $module_id The module ID.
107 + * @param string $state The new state (on/off).
108 + */
109 + public function track_module_option_change( $module_id, $state ): void {
110 + if ( ! $this->is_opted_in() ) {
111 + return;
112 + }
113 +
114 + $enable_module = $state === 'on';
115 + $properties = [
116 + 'context' => 'wp_plugin',
117 + 'option_name' => 'module ' . $module_id,
118 + 'previous_value' => ! $enable_module ? 1 : 0,
119 + 'new_value' => $enable_module ? 1 : 0,
120 + 'language' => $this->user_language,
121 + ];
122 +
123 + $this->track_event( 'Option Changed', $properties );
124 + }
125 +
126 + /**
127 + * Enable or disable usage tracking.
128 + *
129 + * @param bool $enable True to enable, false to disable.
130 + */
131 + public function enable_usage_tracking( $enable ): void {
132 + if ( $enable ) {
133 + $this->optin->enable();
134 + return;
135 + }
136 +
137 + $this->optin->disable();
138 + }
139 +
140 + /**
141 + * Track setup wizard step views.
142 + */
143 + public function track_setup_wizard_step_view() {
144 + if ( ! $this->is_opted_in() ) {
145 + return;
146 + }
147 +
148 + // Get the actual admin page URL from the request referer.
149 + $referer = wp_get_referer();
150 + $current_url = $referer ? $referer : Helper::get_current_page_url();
151 + $path = $this->get_current_path_with_query();
152 +
153 + // Parse the referer URL to get the current step being viewed.
154 + $url_parts = wp_parse_url( $referer );
155 + parse_str( $url_parts['query'] ?? '', $query_params );
156 + $current_step_being_viewed = $query_params['step'] ?? 'compatibility';
157 +
158 + // Use static variable to prevent duplicate tracking in the same request.
159 + static $tracked_steps = [];
160 + if ( in_array( $current_step_being_viewed, $tracked_steps, true ) ) {
161 + return;
162 + }
163 + $tracked_steps[] = $current_step_being_viewed;
164 +
165 + $properties = [
166 + 'current_url' => $current_url,
167 + 'path' => $path,
168 + 'context' => 'wp_plugin',
169 + 'language' => $this->user_language,
170 + ];
171 +
172 + $this->track_event( 'Page Viewed', $properties );
173 + }
174 +
175 + /**
176 + * Enqueue Mixpanel script on Block Editor pages.
177 + */
178 + public function enqueue_mixpanel(): void {
179 + if ( ! $this->optin->can_track() ) {
180 + return;
181 + }
182 +
183 + if ( ! Helper::is_block_editor() || ! Editor::can_add_editor() ) {
184 + return;
185 + }
186 +
187 + $this->mixpanel->add_script();
188 + Helper::add_json(
189 + 'tracking',
190 + [
191 + 'plugin' => $this->plugin,
192 + 'path' => isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '',
193 + 'email' => $this->user_email,
194 + 'language' => $this->user_language,
195 + ],
196 + );
197 + }
198 +
199 + /**
200 + * Track admin page views.
201 + */
202 + public function track_admin_page_view() {
203 + if ( ! $this->optin->can_track() ) {
204 + return;
205 + }
206 +
207 + // Only track Rank Math admin/configuration pages.
208 + if ( ! Str::starts_with( 'rank-math', Param::get( 'page' ) ) ) {
209 + return;
210 + }
211 +
212 + // Check if we've already tracked this user in the last 7 days.
213 + $transient_key = 'rank_math_admin_tracking_' . md5( $this->user_email );
214 + if ( get_transient( $transient_key ) ) {
215 + return;
216 + }
217 +
218 + // Set transient for 7 days to prevent duplicate tracking.
219 + set_transient( $transient_key, true, WEEK_IN_SECONDS );
220 +
221 + // Get the current admin page URL.
222 + $current_url = Helper::get_current_page_url();
223 + $path = $this->get_current_path_with_query();
224 +
225 + $properties = [
226 + 'current_url' => $current_url,
227 + 'path' => $path,
228 + 'context' => 'wp_plugin',
229 + 'language' => $this->user_language,
230 + ];
231 +
232 + // Determine capability based on current Rank Math page and pass it to Mixpanel.
233 + $page = (string) Param::get( 'page' );
234 + $event_capability = $this->get_event_capability_for_page( $page );
235 + $this->track_event( 'Page Viewed', $properties, $event_capability );
236 + }
237 +
238 + /**
239 + * Add usage_tracking option to the general settings.
240 + *
241 + * @param array $json Localized data.
242 + */
243 + public function set_usage_tracking_option( $json ) {
244 + // Early bail if the current page is not general settings.
245 + if ( ! isset( $json['optionPage'] ) || $json['optionPage'] !== 'general' || ! isset( $json['data'] ) ) {
246 + return $json;
247 + }
248 +
249 + $json['canAddUsageTracking'] = current_user_can( 'manage_options' );
250 + $json['data']['usage_tracking'] = $this->optin->can_track();
251 +
252 + return $json;
253 + }
254 +
255 + /**
256 + * Update opt-in value.
257 + *
258 + * @param string $type Settings type.
259 + * @param array $settings Settings data.
260 + */
261 + public function update_mixpanel_optin( $type, $settings ) {
262 + if ( $type !== 'general' || ! isset( $settings['usage_tracking'] ) ) {
263 + return;
264 + }
265 +
266 + if ( ! empty( $settings['usage_tracking'] ) ) {
267 + $this->optin->enable();
268 + return;
269 + }
270 +
271 + $this->optin->disable();
272 + }
273 +
274 + /**
275 + * Add usage tracking data to the saved settings.
276 + *
277 + * @param array $data Settings data.
278 + * @param string $type Settings type.
279 + * @return array
280 + */
281 + public function add_mixpanel_data( $data, $type ) {
282 + if ( $type !== 'general' ) {
283 + return $data;
284 + }
285 +
286 + $data['usage_tracking'] = $this->optin->can_track();
287 + return $data;
288 + }
289 +
290 + /**
291 + * Update Mixpanel optin option when general settings are saved. Used
292 + */
293 + public function update_mixpanel_optin_cmb2() {
294 + // Get the value from the form submission.
295 + $usage_tracking = isset( $_POST['usage_tracking'] ) ? sanitize_text_field( wp_unslash( $_POST['usage_tracking'] ) ) : 'off';
296 + if ( $usage_tracking === 'on' ) {
297 + $this->optin->enable();
298 + return;
299 + }
300 +
301 + $this->optin->disable();
302 + }
303 +
304 + /**
305 + * Check if usage tracking is enabled (opt-in).
306 + *
307 + * @return bool
308 + */
309 + public function is_opted_in(): bool {
310 + return $this->optin->is_enabled();
311 + }
312 +
313 + /**
314 + * Track a custom event for Rank Math.
315 + *
316 + * @param string $event Event name.
317 + * @param array $properties Additional properties to merge.
318 + * @param string $event_capability The capability required to track the event.
319 + */
320 + public function track_event( string $event, array $properties = [], string $event_capability = '' ): void {
321 + $defaults = [
322 + 'context' => 'wp_plugin',
323 + 'language' => $this->user_language,
324 + ];
325 +
326 + $this->identify_user();
327 + $this->mixpanel->track( $event, array_merge( $properties, $defaults ), $event_capability );
328 + }
329 +
330 + /**
331 + * Get the current request path with query string, suitable for tracking.
332 + * Handles AJAX and REST requests by using referer when available.
333 + *
334 + * @return string
335 + */
336 + public function get_current_path_with_query(): string {
337 + // For AJAX/REST requests, use referer to get the originating page.
338 + $referer = wp_get_referer();
339 + $current_url = $referer ? $referer : Helper::get_current_page_url();
340 + $path = wp_parse_url( $current_url, PHP_URL_PATH ) . '?' . wp_parse_url( $current_url, PHP_URL_QUERY );
341 +
342 + return $path;
343 + }
344 +
345 + /**
346 + * Identify the current user in Mixpanel.
347 + * Safe to call multiple times; no-ops when opt-out.
348 + */
349 + public function identify_user(): void {
350 + $this->mixpanel->identify( $this->user_email );
351 + }
352 +
353 + /**
354 + * Get the current plugin label (with version) used in tracking payloads.
355 + *
356 + * @return string
357 + */
358 + public function get_plugin_label(): string {
359 + return $this->plugin;
360 + }
361 +
362 + /**
363 + * Initialize user data.
364 + */
365 + private function init_user_data() {
366 + if ( ! $this->user_email ) {
367 + $this->user_email = $this->get_user_email();
368 + }
369 +
370 + if ( ! $this->user_language ) {
371 + $this->user_language = get_user_locale();
372 + }
373 + }
374 +
375 + /**
376 + * Get user email for identification.
377 + *
378 + * @return string
379 + */
380 + private function get_user_email(): string {
381 + $account = Admin_Helper::get_registration_data();
382 + if ( ! empty( $account['email'] ) ) {
383 + return $account['email'];
384 + }
385 +
386 + $user = wp_get_current_user();
387 + return isset( $user->user_email ) ? (string) $user->user_email : '';
388 + }
389 +
390 + /**
391 + * Get the capability required for tracking based on the current Rank Math page.
392 + *
393 + * @param string $page The `page` query arg, e.g., 'rank-math-options-general'.
394 + *
395 + * @return string Capability name or empty for default behavior.
396 + */
397 + private function get_event_capability_for_page( string $page ): string {
398 + // Map known Rank Math admin pages to their capabilities.
399 + $map = [
400 + 'rank-math-options-general' => 'rank_math_general',
401 + 'rank-math-options-titles' => 'rank_math_titles',
402 + 'rank-math-options-sitemap' => 'rank_math_sitemap',
403 + 'rank-math-options-instant-indexing' => 'rank_math_general',
404 + 'rank-math-404-monitor' => 'rank_math_404_monitor',
405 + 'rank-math-redirections' => 'rank_math_redirections',
406 + 'rank-math-role-manager' => 'rank_math_role_manager',
407 + 'rank-math-analytics' => 'rank_math_analytics',
408 + 'rank-math-seo-analysis' => 'rank_math_site_analysis',
409 + 'rank-math-content-ai-page' => 'rank_math_content_ai',
410 + ];
411 +
412 + return isset( $map[ $page ] ) ? $map[ $page ] : '';
413 + }
414 + }
415 +