PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.0.2
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.0.2
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / includes / abilities / analysis / class-get-integrations-status.php

class-get-integrations-status.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 2.0.2, at includes/abilities/analysis/class-get-integrations-status.php

190 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Get integrations status ability.
4 *
5 * @package ThinkRank\Abilities\Analysis
6 */
7
8 declare(strict_types=1);
9
10 namespace ThinkRank\Abilities\Analysis;
11
12 use ThinkRank\Abilities\Ability_Base;
13 use ThinkRank\Core\Settings;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * Reports the connection status of ThinkRank's Google integrations.
21 *
22 * Covers Google Analytics (GA4), Search Console, and PageSpeed. Returns only
23 * non-sensitive status — connected/configured booleans, the public GA4
24 * measurement ID, and the selected Search Console site. It deliberately never
25 * returns API keys, OAuth access/refresh tokens, or any secret material.
26 */
27 class Get_Integrations_Status extends Ability_Base {
28 /**
29 * Constructor.
30 */
31 public function __construct() {
32 $this->id = 'thinkrank/get-integrations-status';
33 $this->label = __( 'Get Integrations Status', 'thinkrank' );
34 $this->description = __( 'Report the connection status of the Google Analytics, Search Console, and PageSpeed integrations (connected/configured flags, GA4 measurement ID, selected Search Console site). Never returns API keys or OAuth tokens.', 'thinkrank' );
35 }
36
37 /**
38 * {@inheritDoc}
39 *
40 * @return array<string, bool|float|string>
41 */
42 public function get_annotations() {
43 return [
44 'readonly' => true,
45 'destructive' => false,
46 'idempotent' => true,
47 'priority' => 1.0,
48 'openWorldHint' => false,
49 ];
50 }
51
52 /**
53 * {@inheritDoc}
54 *
55 * @return array<string, mixed>
56 */
57 public function get_input_schema() {
58 return [
59 'type' => 'object',
60 'additionalProperties' => false,
61 'properties' => self::empty_properties(),
62 ];
63 }
64
65 /**
66 * {@inheritDoc}
67 *
68 * @return array<string, mixed>
69 */
70 public function get_output_schema() {
71 return [
72 'type' => 'object',
73 'properties' => [
74 'google_account_connected' => [ 'type' => 'boolean' ],
75 'google_analytics' => [
76 'type' => 'object',
77 'additionalProperties' => true,
78 ],
79 'search_console' => [
80 'type' => 'object',
81 'additionalProperties' => true,
82 ],
83 'pagespeed' => [
84 'type' => 'object',
85 'additionalProperties' => true,
86 ],
87 ],
88 ];
89 }
90
91 /**
92 * Execute ability.
93 *
94 * @param array<string, mixed> $input Ability input payload.
95 * @return array<string, mixed>
96 */
97 public function execute( $input ) {
98 $settings = Settings::instance();
99
100 // "Configured" must mean "this integration can actually make a call",
101 // derived from the SAME conditions the client-initialization code uses.
102 //
103 // It previously keyed off three manual `*_api_key` options that no
104 // current code path writes — the OAuth connect flow stores tokens only
105 // (Google_OAuth_Proxy) and the property pickers store property ids — so
106 // an OAuth-connected site actively serving GA4/GSC/PSI data reported
107 // `configured: false` for all three while the same payload reported
108 // `google_account_connected: true`. The report contradicted itself on
109 // the one thing it exists to answer, which is worse than useless to an
110 // agent deciding whether an integration is available.
111 //
112 // The keys are retained as OR-branches: they are optional for data
113 // retrieval but still honored when present (see
114 // Google_PageSpeed_Client::for_site()).
115 $oauth_present = '' !== (string) $settings->get( 'google_access_token', '' );
116
117 // GA4: Analytics_Manager builds the Data API client only when a token
118 // AND a selected property both exist — mirror that exactly.
119 $ga_property = (string) $settings->get( 'seo_analytics_google_analytics_property_id', '' );
120 $ga_configured = ( $oauth_present && '' !== $ga_property )
121 || '' !== (string) $settings->get( 'google_analytics_api_key', '' );
122
123 // Search Console: the client authenticates with the token; the API key
124 // is passed as '' on an OAuth site. A property is not required to
125 // construct it (Analytics_Manager falls back to the site URL).
126 $sc_property = (string) $settings->get( 'search_console_property', '' );
127 $sc_configured = $oauth_present
128 || '' !== (string) $settings->get( 'google_search_console_api_key', '' );
129
130 // PageSpeed: for_site() prefers a dedicated key, else the OAuth token,
131 // else runs keyless on the per-IP quota. Report credentialed state.
132 $ps_key = (string) $settings->get( 'google_pagespeed_api_key', '' );
133 $ps_configured = '' !== $ps_key || $oauth_present;
134
135 // ThinkRank's own tag-injection state (see the google_analytics block).
136 $ga4_measurement_id = (string) $settings->get( 'ga4_measurement_id', '' );
137 $ga4_verified = (bool) $settings->get( 'ga4_tracking_verified', false );
138
139 return [
140 'google_account_connected' => (bool) $settings->get( 'google_account_connected', false ),
141 'google_analytics' => [
142 'configured' => $ga_configured,
143 // The selected GA4 property, in the Admin API's
144 // "properties/XXXXXXXX" form. Empty means no property picked,
145 // which is the usual reason GA is connected but unusable.
146 'property_id' => $ga_property,
147
148 // The two fields below describe ThinkRank's OWN optional
149 // tag-injection feature — NOT whether the site has a working
150 // GA4 tag. On an OAuth-connected site that never used
151 // injection they are legitimately empty/false, and a client
152 // read that as "GA4 tracking is broken" on a site whose tag
153 // was working fine (#250). They are nested and named for what
154 // they actually are so the payload cannot be misread; the
155 // question "can ThinkRank read Analytics data?" is answered by
156 // `configured` above.
157 //
158 // Kept at the top level as well, deprecated, so an existing
159 // consumer does not break on this release.
160 'tag_injection' => [
161 'measurement_id' => $ga4_measurement_id,
162 'auto_inject_enabled' => (bool) $settings->get( 'ga4_auto_inject', false ),
163 'last_check_passed' => $ga4_verified,
164 'last_checked' => (string) $settings->get( 'ga4_last_verification', '' ),
165 ],
166
167 /** @deprecated 1.27.0 Use tag_injection.measurement_id. */
168 'measurement_id' => $ga4_measurement_id,
169 /** @deprecated 1.27.0 Use tag_injection.last_check_passed. */
170 'tracking_verified' => $ga4_verified,
171 ],
172 'search_console' => [
173 'configured' => $sc_configured,
174 // Was reading `google_search_console_site` — a key with no
175 // writer anywhere in the plugin (only two disconnect paths
176 // clear it), so this was unconditionally ''. The property
177 // picker writes `search_console_property`.
178 'site' => $sc_property,
179 ],
180 'pagespeed' => [
181 'configured' => $ps_configured,
182 'oauth_connected' => $oauth_present,
183 // Distinguishes "own key, own quota" from "shared OAuth quota"
184 // — the difference between reliable and 429-prone PSI runs.
185 'has_api_key' => '' !== $ps_key,
186 ],
187 ];
188 }
189 }
190