• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / 0a731db3c07f378783fa4c8a98a311251563afc3

12 Nov 2025 09:39AM UTC coverage: 52.975%. First build
0a731db3c07f378783fa4c8a98a311251563afc3

push

github

thijsoo
Merge branch 'trunk' of github.com:Yoast/wordpress-seo into feature/schema_aggregator

8497 of 15696 branches covered (54.13%)

Branch coverage included in aggregate %.

154 of 269 new or added lines in 36 files covered. (57.25%)

32187 of 61102 relevant lines covered (52.68%)

39085.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

49.35
/src/dashboard/infrastructure/integrations/site-kit.php
1
<?php
2
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
3
namespace Yoast\WP\SEO\Dashboard\Infrastructure\Integrations;
4

5
use Google\Site_Kit\Core\REST_API\REST_Routes;
6
use Yoast\WP\SEO\Conditionals\Google_Site_Kit_Feature_Conditional;
7
use Yoast\WP\SEO\Conditionals\Third_Party\Site_Kit_Conditional;
8
use Yoast\WP\SEO\Dashboard\Infrastructure\Configuration\Permanently_Dismissed_Site_Kit_Configuration_Repository_Interface as Configuration_Repository;
9
use Yoast\WP\SEO\Dashboard\Infrastructure\Configuration\Site_Kit_Consent_Repository_Interface;
10
use Yoast\WP\SEO\Dashboard\Infrastructure\Connection\Site_Kit_Is_Connected_Call;
11
use Yoast\WP\SEO\Dashboard\User_Interface\Setup\Setup_Url_Interceptor;
12

13
/**
14
 * Describes if the Site kit integration is enabled and configured.
15
 */
16
class Site_Kit {
17

18
        private const SITE_KIT_FILE = 'google-site-kit/google-site-kit.php';
19

20
        /**
21
         * The Site Kit feature conditional.
22
         *
23
         * @var Google_Site_Kit_Feature_Conditional
24
         */
25
        protected $site_kit_feature_conditional;
26

27
        /**
28
         * The Site Kit conditional.
29
         *
30
         * @var Site_Kit_Conditional
31
         */
32
        private $site_kit_conditional;
33

34
        /**
35
         * The Site Kit consent repository.
36
         *
37
         * @var Site_Kit_Consent_Repository_Interface
38
         */
39
        private $site_kit_consent_repository;
40

41
        /**
42
         * The Site Kit consent repository.
43
         *
44
         * @var Configuration_Repository
45
         */
46
        private $permanently_dismissed_site_kit_configuration_repository;
47

48
        /**
49
         * The call wrapper.
50
         *
51
         * @var Site_Kit_Is_Connected_Call $site_kit_is_connected_call
52
         */
53
        private $site_kit_is_connected_call;
54

55
        /**
56
         * The search console module data.
57
         *
58
         * @var array<string, bool> $search_console_module
59
         */
60
        private $search_console_module = [
61
                'can_view' => null,
62
        ];
63

64
        /**
65
         * The analytics module data.
66
         *
67
         * @var array<string, bool> $ga_module
68
         */
69
        private $ga_module = [
70
                'can_view'  => null,
71
                'connected' => null,
72
        ];
73

74
        /**
75
         * The constructor.
76
         *
77
         * @param Site_Kit_Consent_Repository_Interface $site_kit_consent_repository  The Site Kit consent repository.
78
         * @param Configuration_Repository              $configuration_repository     The Site Kit permanently dismissed
79
         *                                                                            configuration repository.
80
         * @param Site_Kit_Is_Connected_Call            $site_kit_is_connected_call   The api call to check if the site is
81
         *                                                                            connected.
82
         * @param Google_Site_Kit_Feature_Conditional   $site_kit_feature_conditional The Site Kit feature conditional.
83
         * @param Site_Kit_Conditional                  $site_kit_conditional         The Site Kit conditional.
84
         */
85
        public function __construct(
×
86
                Site_Kit_Consent_Repository_Interface $site_kit_consent_repository,
87
                Configuration_Repository $configuration_repository,
88
                Site_Kit_Is_Connected_Call $site_kit_is_connected_call,
89
                Google_Site_Kit_Feature_Conditional $site_kit_feature_conditional,
90
                Site_Kit_Conditional $site_kit_conditional
91
        ) {
92
                $this->site_kit_consent_repository                             = $site_kit_consent_repository;
×
93
                $this->permanently_dismissed_site_kit_configuration_repository = $configuration_repository;
×
94
                $this->site_kit_is_connected_call                              = $site_kit_is_connected_call;
×
95
                $this->site_kit_feature_conditional                            = $site_kit_feature_conditional;
×
NEW
96
                $this->site_kit_conditional                                    = $site_kit_conditional;
×
97
        }
98

99
        /**
100
         * If the Site Kit plugin is active.
101
         *
102
         * @return bool If the integration is activated.
103
         */
104
        public function is_enabled(): bool {
18✔
105
                return $this->site_kit_conditional->is_met();
18✔
106
        }
107

108
        /**
109
         * If the Google site kit setup has been completed.
110
         *
111
         * @return bool If the Google site kit setup has been completed.
112
         */
113
        private function is_setup_completed(): bool {
×
114
                return $this->site_kit_is_connected_call->is_setup_completed();
×
115
        }
116

117
        /**
118
         * If consent has been granted.
119
         *
120
         * @return bool If consent has been granted.
121
         */
122
        private function is_connected(): bool {
14✔
123
                return $this->site_kit_consent_repository->is_consent_granted();
14✔
124
        }
125

126
        /**
127
         * If Google Analytics is connected.
128
         *
129
         * @return bool If Google Analytics is connected.
130
         */
131
        public function is_ga_connected(): bool {
18✔
132
                if ( $this->ga_module['connected'] !== null ) {
18✔
133
                        return $this->ga_module['connected'];
10✔
134
                }
135

136
                return $this->site_kit_is_connected_call->is_ga_connected();
8✔
137
        }
138

139
        /**
140
         * If the Site Kit plugin is installed. This is needed since we cannot check with `is_plugin_active` in rest
141
         * requests. `Plugin.php` is only loaded on admin pages.
142
         *
143
         * @return bool If the Site Kit plugin is installed.
144
         */
145
        private function is_site_kit_installed(): bool {
×
146
                return \class_exists( 'Google\Site_Kit\Plugin' );
×
147
        }
148

149
        /**
150
         * If the entire onboarding has been completed.
151
         *
152
         * @return bool If the entire onboarding has been completed.
153
         */
154
        public function is_onboarded(): bool {
×
155
                // @TODO: Consider replacing the `is_setup_completed()` check with a `can_read_data( $module )` check (and possibly rename the method to something more genric eg. is_ready() ).
156
                return ( $this->is_site_kit_installed() && $this->is_setup_completed() && $this->is_connected() );
×
157
        }
158

159
        /**
160
         * Checks if current user can view dashboard data for a module
161
         *
162
         * @param array<array|null> $module The module.
163
         *
164
         * @return bool If the user can read the data.
165
         */
166
        private function can_read_data( array $module ): bool {
14✔
167
                return ( ! \is_null( $module['can_view'] ) ? $module['can_view'] : false );
14✔
168
        }
169

170
        /**
171
         * Return this object represented by a key value array.
172
         *
173
         * @return array<string, bool> Returns the name and if the feature is enabled.
174
         */
175
        public function to_array(): array {
16✔
176
                if ( ! $this->site_kit_feature_conditional->is_met() ) {
16✔
177
                        return [];
2✔
178
                }
179
                if ( $this->is_enabled() ) {
14✔
180
                        $this->parse_site_kit_data();
10✔
181
                }
182
                return [
14✔
183
                        'installUrl'               => \self_admin_url( 'update.php?page=' . Setup_Url_Interceptor::PAGE . '&redirect_setup_url=' ) . \rawurlencode( $this->get_install_url() ),
14✔
184
                        'activateUrl'              => \self_admin_url( 'update.php?page=' . Setup_Url_Interceptor::PAGE . '&redirect_setup_url=' ) . \rawurlencode( $this->get_activate_url() ),
14✔
185
                        'setupUrl'                 => \self_admin_url( 'update.php?page=' . Setup_Url_Interceptor::PAGE . '&redirect_setup_url=' ) . \rawurlencode( $this->get_setup_url() ),
14✔
186
                        'updateUrl'                => \self_admin_url( 'update.php?page=' . Setup_Url_Interceptor::PAGE . '&redirect_setup_url=' ) . \rawurlencode( $this->get_update_url() ),
14✔
187
                        'dashboardUrl'             => \self_admin_url( 'admin.php?page=googlesitekit-dashboard' ),
14✔
188
                        'isAnalyticsConnected'     => $this->is_ga_connected(),
14✔
189
                        'isFeatureEnabled'         => true,
14✔
190
                        'isSetupWidgetDismissed'   => $this->permanently_dismissed_site_kit_configuration_repository->is_site_kit_configuration_dismissed(),
14✔
191
                        'capabilities'             => [
14✔
192
                                'installPlugins'        => \current_user_can( 'install_plugins' ),
14✔
193
                                'viewSearchConsoleData' => $this->can_read_data( $this->search_console_module ),
14✔
194
                                'viewAnalyticsData'     => $this->can_read_data( $this->ga_module ),
14✔
195
                        ],
14✔
196
                        'connectionStepsStatuses'  => [
14✔
197
                                'isInstalled'      => \file_exists( \WP_PLUGIN_DIR . '/' . self::SITE_KIT_FILE ),
14✔
198
                                'isActive'         => $this->is_enabled(),
14✔
199
                                'isSetupCompleted' => $this->can_read_data( $this->search_console_module ) || $this->can_read_data( $this->ga_module ),
14✔
200
                                'isConsentGranted' => $this->is_connected(),
14✔
201
                        ],
14✔
202
                        'isVersionSupported'       => \defined( 'GOOGLESITEKIT_VERSION' ) ? \version_compare( \GOOGLESITEKIT_VERSION, '1.148.0', '>=' ) : false,
14✔
203
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
204
                        'isRedirectedFromSiteKit'  => isset( $_GET['redirected_from_site_kit'] ),
14✔
205
                ];
14✔
206
        }
207

208
        /**
209
         * Return this object represented by a key value array. This is not used yet.
210
         *
211
         * @codeCoverageIgnore
212
         *
213
         * @return array<string, bool> Returns the name and if the feature is enabled.
214
         */
215
        public function to_legacy_array(): array {
216
                return $this->to_array();
217
        }
218

219
        /**
220
         * Parses the Site Kit configuration data.
221
         *
222
         * @return void
223
         */
224
        public function parse_site_kit_data(): void {
×
225
                $paths     = $this->get_preload_paths();
×
226
                $preloaded = $this->get_preloaded_data( $paths );
×
227
                if ( empty( $preloaded ) ) {
×
228
                        return;
×
229
                }
230

231
                $modules_data        = ! empty( $preloaded[ $paths['modules'] ]['body'] ) ? $preloaded[ $paths['modules'] ]['body'] : [];
×
232
                $modules_permissions = ! empty( $preloaded[ $paths['permissions'] ]['body'] ) ? $preloaded[ $paths['permissions'] ]['body'] : [];
×
233

234
                $can_view_dashboard = ( $modules_permissions['googlesitekit_view_authenticated_dashboard'] ?? false );
×
235

236
                foreach ( $modules_data as $module ) {
×
237
                        $slug = $module['slug'];
×
238
                        // We have to also check if the module is recoverable, because if we rely on the module being shared, we have to make also sure the module owner is still connected.
239
                        $is_recoverable = ( $module['recoverable'] ?? null );
×
240

241
                        if ( $slug === 'analytics-4' ) {
×
242
                                $can_read_shared_module_data = ( $modules_permissions['googlesitekit_read_shared_module_data::["analytics-4"]'] ?? false );
×
243

244
                                $this->ga_module['can_view']  = $can_view_dashboard || ( $can_read_shared_module_data && ! $is_recoverable );
×
245
                                $this->ga_module['connected'] = ( $module['connected'] ?? false );
×
246
                        }
247

248
                        if ( $slug === 'search-console' ) {
×
249
                                $can_read_shared_module_data = ( $modules_permissions['googlesitekit_read_shared_module_data::["search-console"]'] ?? false );
×
250

251
                                $this->search_console_module['can_view'] = $can_view_dashboard || ( $can_read_shared_module_data && ! $is_recoverable );
×
252
                        }
253
                }
254
        }
255

256
        /**
257
         * Holds the parsed preload paths for preloading some Site Kit API data.
258
         *
259
         * @return string[]
260
         */
261
        public function get_preload_paths(): array {
×
262

263
                $rest_root = ( \class_exists( REST_Routes::class ) ) ? REST_Routes::REST_ROOT : '';
×
264

265
                return [
×
266
                        'permissions'    => '/' . $rest_root . '/core/user/data/permissions',
×
267
                        'modules'        => '/' . $rest_root . '/core/modules/data/list',
×
268
                ];
×
269
        }
270

271
        /**
272
         * Runs the given paths through the `rest_preload_api_request` method.
273
         *
274
         * @param string[] $paths The paths to add to `rest_preload_api_request`.
275
         *
276
         * @return array<array|null> The array with all the now filled in preloaded data.
277
         */
278
        public function get_preloaded_data( array $paths ): array {
×
279
                $preload_paths = \apply_filters( 'googlesitekit_apifetch_preload_paths', [] );
×
280
                $actual_paths  = \array_intersect( $paths, $preload_paths );
×
281

282
                return \array_reduce(
283
                        \array_unique( $actual_paths ),
284
                        'rest_preload_api_request',
285
                        []
286
                );
287
        }
288

289
        /**
290
         * Creates a valid activation URL for the Site Kit plugin.
291
         *
292
         * @return string
293
         */
294
        public function get_activate_url(): string {
295
                return \html_entity_decode(
296
                        \wp_nonce_url(
297
                                \self_admin_url( 'plugins.php?action=activate&plugin=' . self::SITE_KIT_FILE ),
298
                                'activate-plugin_' . self::SITE_KIT_FILE
299
                        )
300
                );
301
        }
302

303
        /**
304
         *  Creates a valid install URL for the Site Kit plugin.
305
         *
306
         * @return string
307
         */
308
        public function get_install_url(): string {
309
                return \html_entity_decode(
310
                        \wp_nonce_url(
311
                                \self_admin_url( 'update.php?action=install-plugin&plugin=google-site-kit' ),
312
                                'install-plugin_google-site-kit'
313
                        )
314
                );
315
        }
316

317
        /**
318
         *  Creates a valid update URL for the Site Kit plugin.
319
         *
320
         * @return string
321
         */
322
        public function get_update_url(): string {
323
                return \html_entity_decode(
324
                        \wp_nonce_url(
325
                                \self_admin_url( 'update.php?action=upgrade-plugin&plugin=' . self::SITE_KIT_FILE ),
326
                                'upgrade-plugin_' . self::SITE_KIT_FILE
327
                        )
328
                );
329
        }
330

331
        /**
332
         *  Creates a valid setup URL for the Site Kit plugin.
333
         *
334
         * @return string
335
         */
336
        public function get_setup_url(): string {
337
                return \self_admin_url( 'admin.php?page=googlesitekit-splash' );
338
        }
339
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc