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

Yoast / wordpress-seo / 329e7257b699570577e9617a4ea672714686da15

26 Jan 2026 07:52AM UTC coverage: 52.923% (-0.4%) from 53.35%
329e7257b699570577e9617a4ea672714686da15

Pull #22888

github

web-flow
Merge 00d428c6c into ff2f33de1
Pull Request #22888: Add integration tests for analytics adapter

8393 of 15825 branches covered (53.04%)

Branch coverage included in aggregate %.

32383 of 61223 relevant lines covered (52.89%)

47097.05 hits per line

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

48.65
/src/dashboard/infrastructure/integrations/site-kit.php
1
<?php
2

3
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
4
namespace Yoast\WP\SEO\Dashboard\Infrastructure\Integrations;
5

6
use Google\Site_Kit\Core\REST_API\REST_Routes;
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 conditional.
22
         *
23
         * @var Site_Kit_Conditional
24
         */
25
        private $site_kit_conditional;
26

27
        /**
28
         * The Site Kit consent repository.
29
         *
30
         * @var Site_Kit_Consent_Repository_Interface
31
         */
32
        private $site_kit_consent_repository;
33

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

41
        /**
42
         * The call wrapper.
43
         *
44
         * @var Site_Kit_Is_Connected_Call $site_kit_is_connected_call
45
         */
46
        private $site_kit_is_connected_call;
47

48
        /**
49
         * The search console module data.
50
         *
51
         * @var array<string, bool> $search_console_module
52
         */
53
        private $search_console_module = [
54
                'can_view' => null,
55
        ];
56

57
        /**
58
         * The analytics module data.
59
         *
60
         * @var array<string, bool> $ga_module
61
         */
62
        private $ga_module = [
63
                'can_view'  => null,
64
                'connected' => null,
65
        ];
66

67
        /**
68
         * The constructor.
69
         *
70
         * @param Site_Kit_Consent_Repository_Interface $site_kit_consent_repository The Site Kit consent repository.
71
         * @param Configuration_Repository              $configuration_repository    The Site Kit permanently dismissed
72
         *                                                                           configuration repository.
73
         * @param Site_Kit_Is_Connected_Call            $site_kit_is_connected_call  The api call to check if the site is
74
         *                                                                           connected.
75
         * @param Site_Kit_Conditional                  $site_kit_conditional        The Site Kit conditional.
76
         */
77
        public function __construct(
×
78
                Site_Kit_Consent_Repository_Interface $site_kit_consent_repository,
79
                Configuration_Repository $configuration_repository,
80
                Site_Kit_Is_Connected_Call $site_kit_is_connected_call,
81
                Site_Kit_Conditional $site_kit_conditional
82
        ) {
83
                $this->site_kit_consent_repository                             = $site_kit_consent_repository;
×
84
                $this->permanently_dismissed_site_kit_configuration_repository = $configuration_repository;
×
85
                $this->site_kit_is_connected_call                              = $site_kit_is_connected_call;
×
86
                $this->site_kit_conditional                                    = $site_kit_conditional;
×
87
        }
88

89
        /**
90
         * If the Site Kit plugin is active.
91
         *
92
         * @return bool If the integration is activated.
93
         */
94
        public function is_enabled(): bool {
18✔
95
                return $this->site_kit_conditional->is_met();
18✔
96
        }
97

98
        /**
99
         * If the Google site kit setup has been completed.
100
         *
101
         * @return bool If the Google site kit setup has been completed.
102
         */
103
        private function is_setup_completed(): bool {
×
104
                return $this->site_kit_is_connected_call->is_setup_completed();
×
105
        }
106

107
        /**
108
         * If consent has been granted.
109
         *
110
         * @return bool If consent has been granted.
111
         */
112
        private function is_connected(): bool {
14✔
113
                return $this->site_kit_consent_repository->is_consent_granted();
14✔
114
        }
115

116
        /**
117
         * If Google Analytics is connected.
118
         *
119
         * @return bool If Google Analytics is connected.
120
         */
121
        public function is_ga_connected(): bool {
18✔
122
                if ( $this->ga_module['connected'] !== null ) {
18✔
123
                        return $this->ga_module['connected'];
10✔
124
                }
125

126
                return $this->site_kit_is_connected_call->is_ga_connected();
8✔
127
        }
128

129
        /**
130
         * If the Site Kit plugin is installed. This is needed since we cannot check with `is_plugin_active` in rest
131
         * requests. `Plugin.php` is only loaded on admin pages.
132
         *
133
         * @return bool If the Site Kit plugin is installed.
134
         */
135
        private function is_site_kit_installed(): bool {
×
136
                return \class_exists( 'Google\Site_Kit\Plugin' );
×
137
        }
138

139
        /**
140
         * If the entire onboarding has been completed.
141
         *
142
         * @return bool If the entire onboarding has been completed.
143
         */
144
        public function is_onboarded(): bool {
×
145
                // @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() ).
146
                return ( $this->is_site_kit_installed() && $this->is_setup_completed() && $this->is_connected() );
×
147
        }
148

149
        /**
150
         * Checks if current user can view dashboard data for a module
151
         *
152
         * @param array<array|null> $module The module.
153
         *
154
         * @return bool If the user can read the data.
155
         */
156
        private function can_read_data( array $module ): bool {
14✔
157
                return ( ! \is_null( $module['can_view'] ) ? $module['can_view'] : false );
14✔
158
        }
159

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

195
        /**
196
         * Return this object represented by a key value array. This is not used yet.
197
         *
198
         * @codeCoverageIgnore
199
         *
200
         * @return array<string, bool> Returns the name and if the feature is enabled.
201
         */
202
        public function to_legacy_array(): array {
203
                return $this->to_array();
204
        }
205

206
        /**
207
         * Parses the Site Kit configuration data.
208
         *
209
         * @return void
210
         */
211
        public function parse_site_kit_data(): void {
×
212
                $paths     = $this->get_preload_paths();
×
213
                $preloaded = $this->get_preloaded_data( $paths );
×
214
                if ( empty( $preloaded ) ) {
×
215
                        return;
×
216
                }
217

218
                $modules_data        = ! empty( $preloaded[ $paths['modules'] ]['body'] ) ? $preloaded[ $paths['modules'] ]['body'] : [];
×
219
                $modules_permissions = ! empty( $preloaded[ $paths['permissions'] ]['body'] ) ? $preloaded[ $paths['permissions'] ]['body'] : [];
×
220

221
                $can_view_dashboard = ( $modules_permissions['googlesitekit_view_authenticated_dashboard'] ?? false );
×
222

223
                foreach ( $modules_data as $module ) {
×
224
                        $slug = $module['slug'];
×
225
                        // 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.
226
                        $is_recoverable = ( $module['recoverable'] ?? null );
×
227

228
                        if ( $slug === 'analytics-4' ) {
×
229
                                $can_read_shared_module_data = ( $modules_permissions['googlesitekit_read_shared_module_data::["analytics-4"]'] ?? false );
×
230

231
                                $this->ga_module['can_view']  = $can_view_dashboard || ( $can_read_shared_module_data && ! $is_recoverable );
×
232
                                $this->ga_module['connected'] = ( $module['connected'] ?? false );
×
233
                        }
234

235
                        if ( $slug === 'search-console' ) {
×
236
                                $can_read_shared_module_data = ( $modules_permissions['googlesitekit_read_shared_module_data::["search-console"]'] ?? false );
×
237

238
                                $this->search_console_module['can_view'] = $can_view_dashboard || ( $can_read_shared_module_data && ! $is_recoverable );
×
239
                        }
240
                }
241
        }
242

243
        /**
244
         * Holds the parsed preload paths for preloading some Site Kit API data.
245
         *
246
         * @return string[]
247
         */
248
        public function get_preload_paths(): array {
×
249

250
                $rest_root = ( \class_exists( REST_Routes::class ) ) ? REST_Routes::REST_ROOT : '';
×
251

252
                return [
×
253
                        'permissions'    => '/' . $rest_root . '/core/user/data/permissions',
×
254
                        'modules'        => '/' . $rest_root . '/core/modules/data/list',
×
255
                ];
×
256
        }
257

258
        /**
259
         * Runs the given paths through the `rest_preload_api_request` method.
260
         *
261
         * @param string[] $paths The paths to add to `rest_preload_api_request`.
262
         *
263
         * @return array<array|null> The array with all the now filled in preloaded data.
264
         */
265
        public function get_preloaded_data( array $paths ): array {
×
266
                $preload_paths = \apply_filters( 'googlesitekit_apifetch_preload_paths', [] );
×
267
                $actual_paths  = \array_intersect( $paths, $preload_paths );
×
268

269
                return \array_reduce(
270
                        \array_unique( $actual_paths ),
271
                        'rest_preload_api_request',
272
                        []
273
                );
274
        }
275

276
        /**
277
         * Creates a valid activation URL for the Site Kit plugin.
278
         *
279
         * @return string
280
         */
281
        public function get_activate_url(): string {
282
                return \html_entity_decode(
283
                        \wp_nonce_url(
284
                                \self_admin_url( 'plugins.php?action=activate&plugin=' . self::SITE_KIT_FILE ),
285
                                'activate-plugin_' . self::SITE_KIT_FILE
286
                        )
287
                );
288
        }
289

290
        /**
291
         *  Creates a valid install URL for the Site Kit plugin.
292
         *
293
         * @return string
294
         */
295
        public function get_install_url(): string {
296
                return \html_entity_decode(
297
                        \wp_nonce_url(
298
                                \self_admin_url( 'update.php?action=install-plugin&plugin=google-site-kit' ),
299
                                'install-plugin_google-site-kit'
300
                        )
301
                );
302
        }
303

304
        /**
305
         *  Creates a valid update URL for the Site Kit plugin.
306
         *
307
         * @return string
308
         */
309
        public function get_update_url(): string {
310
                return \html_entity_decode(
311
                        \wp_nonce_url(
312
                                \self_admin_url( 'update.php?action=upgrade-plugin&plugin=' . self::SITE_KIT_FILE ),
313
                                'upgrade-plugin_' . self::SITE_KIT_FILE
314
                        )
315
                );
316
        }
317

318
        /**
319
         *  Creates a valid setup URL for the Site Kit plugin.
320
         *
321
         * @return string
322
         */
323
        public function get_setup_url(): string {
324
                return \self_admin_url( 'admin.php?page=googlesitekit-splash' );
325
        }
326
}
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