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

Yoast / wordpress-seo / bc0ef10d026f427d71d172d89244523ffe9c01f2

09 May 2025 06:33AM UTC coverage: 52.42% (-0.4%) from 52.795%
bc0ef10d026f427d71d172d89244523ffe9c01f2

push

github

mykola
Merge remote-tracking branch 'origin/trunk' into feature/ai-optimize-classic

7862 of 13947 branches covered (56.37%)

Branch coverage included in aggregate %.

2 of 8 new or added lines in 1 file covered. (25.0%)

3 existing lines in 1 file now uncovered.

29109 of 56581 relevant lines covered (51.45%)

42197.27 hits per line

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

50.0
/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\Dashboard\Infrastructure\Configuration\Permanently_Dismissed_Site_Kit_Configuration_Repository_Interface as Configuration_Repository;
8
use Yoast\WP\SEO\Dashboard\Infrastructure\Configuration\Site_Kit_Consent_Repository_Interface;
9
use Yoast\WP\SEO\Dashboard\Infrastructure\Connection\Site_Kit_Is_Connected_Call;
10
use Yoast\WP\SEO\Dashboard\User_Interface\Setup\Setup_Url_Interceptor;
11

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

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

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

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

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

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

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

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

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

88
        /**
89
         * If the integration is activated.
90
         *
91
         * @return bool If the integration is activated.
92
         */
93
        public function is_enabled(): bool {
18✔
94
                return \is_plugin_active( self::SITE_KIT_FILE );
18✔
95
        }
96

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

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

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

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

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

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

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

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

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

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

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

NEW
223
                $can_view_dashboard = ( $modules_permissions['googlesitekit_view_authenticated_dashboard'] ?? false );
×
224

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

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

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

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

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

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

252
                $rest_root = ( \class_exists( REST_Routes::class ) ) ? REST_Routes::REST_ROOT : '';
×
253

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

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

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

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

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

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

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