PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.8.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.8.2
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / REST / AnalyticsTracker.php

AnalyticsTracker.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.8.2, at includes/REST/AnalyticsTracker.php

96 lines 2.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\REST;
4
5 use WP_REST_Request;
6 use WP_REST_Response;
7 use WPDeveloper\BetterDocs\Core\BaseAPI;
8 use WPDeveloper\BetterDocs\Core\AnalyticsTracker as Tracker;
9
10 /**
11 * Write-only ingest for the lightweight Free view tracker.
12 *
13 * POST betterdocs/v1/analytics/view { post_id, u, nonce }
14 *
15 * Kept intentionally thin: verify nonce, validate the doc, hand off to the
16 * Core tracker's record_view(). No reads/joins beyond the single upsert.
17 */
18 class AnalyticsTracker extends BaseAPI {
19 public function register() {
20 $this->post(
21 '/analytics/view',
22 [ $this, 'track' ],
23 [
24 'post_id' => [
25 'type' => 'integer',
26 'required' => true,
27 'validate_callback' => function ( $param ) {
28 return ! empty( $param ) && is_numeric( $param ) && get_post( (int) $param ) !== null;
29 }
30 ],
31 'u' => [
32 'type' => 'integer',
33 'required' => false,
34 'default' => 0
35 ]
36 ]
37 );
38 }
39
40 /**
41 * Public endpoint, gated by a wp_rest nonce. The tracker sends it via the
42 * X-WP-Nonce header (with credentials), which also satisfies WordPress's
43 * global REST cookie nonce check for logged-in visitors. Falls back to a
44 * _wpnonce request param.
45 */
46 public function permission_check( $request = null ) {
47 if ( ! $request instanceof WP_REST_Request ) {
48 return false;
49 }
50
51 $nonce = $request->get_header( 'x_wp_nonce' );
52 if ( empty( $nonce ) ) {
53 $nonce = $request->get_param( '_wpnonce' );
54 }
55
56 return (bool) wp_verify_nonce( $nonce, 'wp_rest' );
57 }
58
59 public function track( WP_REST_Request $request ) {
60 $post_id = (int) $request->get_param( 'post_id' );
61 $tracker = $this->container->get( Tracker::class );
62
63 // Reading-completion signal: a scroll-depth update for an already-counted
64 // view. Does not record a new view (no double count).
65 if ( $request->get_param( 'event' ) === 'scroll' ) {
66 $depth = max( 0, min( 100, (int) $request->get_param( 'depth' ) ) );
67 if ( get_post( $post_id ) && $tracker->is_eligible() ) {
68 /**
69 * Fires with a doc's max scroll depth (0–100) on page hide.
70 * Pro records it as a 'scroll' event for the reading-completion rollup.
71 */
72 do_action( 'betterdocs_analytics_scroll_recorded', $post_id, $depth );
73 }
74 return new WP_REST_Response( [ 'success' => true ], 200 );
75 }
76
77 $unique = (int) $request->get_param( 'u' );
78
79 $recorded = $tracker->record_view( $post_id, $unique );
80
81 if ( $recorded ) {
82 /**
83 * Fires after a doc view is recorded into the simple aggregate.
84 * Pro hooks this to write an enriched row into the raw events table
85 * (referrer, device, kb, language, hashed ip/session).
86 *
87 * @param int $post_id Doc post id.
88 * @param WP_REST_Request $request The ingest request (carries referrer).
89 */
90 do_action( 'betterdocs_analytics_view_recorded', $post_id, $request );
91 }
92
93 return new WP_REST_Response( [ 'success' => (bool) $recorded ], 200 );
94 }
95 }
96