• 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

0.0
/src/integrations/admin/first-time-configuration-integration.php
1
<?php
2

3
namespace Yoast\WP\SEO\Integrations\Admin;
4

5
use WP_User;
6
use WPSEO_Addon_Manager;
7
use WPSEO_Admin_Asset_Manager;
8
use WPSEO_Option_Tab;
9
use WPSEO_Shortlinker;
10
use Yoast\WP\SEO\Conditionals\Admin_Conditional;
11
use Yoast\WP\SEO\Context\Meta_Tags_Context;
12
use Yoast\WP\SEO\General\User_Interface\General_Page_Integration;
13
use Yoast\WP\SEO\Helpers\Options_Helper;
14
use Yoast\WP\SEO\Helpers\Product_Helper;
15
use Yoast\WP\SEO\Helpers\Social_Profiles_Helper;
16
use Yoast\WP\SEO\Helpers\Woocommerce_Helper;
17
use Yoast\WP\SEO\Integrations\Integration_Interface;
18
use Yoast\WP\SEO\Routes\Indexing_Route;
19

20
/**
21
 * First_Time_Configuration_Integration class
22
 */
23
class First_Time_Configuration_Integration implements Integration_Interface {
24

25
        /**
26
         * The admin asset manager.
27
         *
28
         * @var WPSEO_Admin_Asset_Manager
29
         */
30
        private $admin_asset_manager;
31

32
        /**
33
         * The addon manager.
34
         *
35
         * @var WPSEO_Addon_Manager
36
         */
37
        private $addon_manager;
38

39
        /**
40
         * The shortlinker.
41
         *
42
         * @var WPSEO_Shortlinker
43
         */
44
        private $shortlinker;
45

46
        /**
47
         * The options' helper.
48
         *
49
         * @var Options_Helper
50
         */
51
        private $options_helper;
52

53
        /**
54
         * The social profiles helper.
55
         *
56
         * @var Social_Profiles_Helper
57
         */
58
        private $social_profiles_helper;
59

60
        /**
61
         * The product helper.
62
         *
63
         * @var Product_Helper
64
         */
65
        private $product_helper;
66

67
        /**
68
         * The meta tags context helper.
69
         *
70
         * @var Meta_Tags_Context
71
         */
72
        private $meta_tags_context;
73

74
        /**
75
         * The WooCommerce helper.
76
         *
77
         * @var Woocommerce_Helper
78
         */
79
        private $woocommerce_helper;
80

81
        /**
82
         * {@inheritDoc}
83
         */
84
        public static function get_conditionals() {
×
85
                return [ Admin_Conditional::class ];
×
86
        }
87

88
        /**
89
         * First_Time_Configuration_Integration constructor.
90
         *
91
         * @param WPSEO_Admin_Asset_Manager $admin_asset_manager    The admin asset manager.
92
         * @param WPSEO_Addon_Manager       $addon_manager          The addon manager.
93
         * @param WPSEO_Shortlinker         $shortlinker            The shortlinker.
94
         * @param Options_Helper            $options_helper         The options helper.
95
         * @param Social_Profiles_Helper    $social_profiles_helper The social profile helper.
96
         * @param Product_Helper            $product_helper         The product helper.
97
         * @param Meta_Tags_Context         $meta_tags_context      The meta tags context helper.
98
         * @param Woocommerce_Helper        $woocommerce_helper     The WooCommerce helper.
99
         */
100
        public function __construct(
×
101
                WPSEO_Admin_Asset_Manager $admin_asset_manager,
102
                WPSEO_Addon_Manager $addon_manager,
103
                WPSEO_Shortlinker $shortlinker,
104
                Options_Helper $options_helper,
105
                Social_Profiles_Helper $social_profiles_helper,
106
                Product_Helper $product_helper,
107
                Meta_Tags_Context $meta_tags_context,
108
                Woocommerce_Helper $woocommerce_helper
109
        ) {
110
                $this->admin_asset_manager    = $admin_asset_manager;
×
111
                $this->addon_manager          = $addon_manager;
×
112
                $this->shortlinker            = $shortlinker;
×
113
                $this->options_helper         = $options_helper;
×
114
                $this->social_profiles_helper = $social_profiles_helper;
×
115
                $this->product_helper         = $product_helper;
×
116
                $this->meta_tags_context      = $meta_tags_context;
×
NEW
117
                $this->woocommerce_helper     = $woocommerce_helper;
×
118
        }
119

120
        /**
121
         * {@inheritDoc}
122
         */
123
        public function register_hooks() {
×
124
                \add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
×
125
                \add_action( 'wpseo_settings_tabs_dashboard', [ $this, 'add_first_time_configuration_tab' ] );
×
126
        }
127

128
        /**
129
         * Adds a dedicated tab in the General sub-page.
130
         *
131
         * @param WPSEO_Options_Tabs $dashboard_tabs Object representing the tabs of the General sub-page.
132
         *
133
         * @return void
134
         */
135
        public function add_first_time_configuration_tab( $dashboard_tabs ) {
×
136
                $dashboard_tabs->add_tab(
×
137
                        new WPSEO_Option_Tab(
×
138
                                'first-time-configuration',
×
139
                                \__( 'First-time configuration', 'wordpress-seo' ),
×
140
                                [ 'save_button' => false ]
×
141
                        )
×
142
                );
×
143
        }
144

145
        /**
146
         * Adds the data for the first-time configuration to the wpseoFirstTimeConfigurationData object.
147
         *
148
         * @return void
149
         */
150
        public function enqueue_assets() {
×
151
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Date is not processed or saved.
152
                if ( ! isset( $_GET['page'] ) || ( $_GET['page'] !== 'wpseo_dashboard' && $_GET['page'] !== General_Page_Integration::PAGE ) || \is_network_admin() ) {
×
153
                        return;
×
154
                }
155

156
                $this->admin_asset_manager->enqueue_script( 'indexation' );
×
157
                $this->admin_asset_manager->enqueue_style( 'first-time-configuration' );
×
158
                $this->admin_asset_manager->enqueue_style( 'admin-css' );
×
159
                $this->admin_asset_manager->enqueue_style( 'monorepo' );
×
160

161
                $data = [
×
162
                        'disabled'     => ! \YoastSEO()->helpers->indexable->should_index_indexables(),
×
163
                        'amount'       => \YoastSEO()->helpers->indexing->get_filtered_unindexed_count(),
×
164
                        'firstTime'    => ( \YoastSEO()->helpers->indexing->is_initial_indexing() === true ),
×
165
                        'errorMessage' => '',
×
166
                        'restApi'      => [
×
167
                                'root'               => \esc_url_raw( \rest_url() ),
×
168
                                'indexing_endpoints' => $this->get_endpoints(),
×
169
                                'nonce'              => \wp_create_nonce( 'wp_rest' ),
×
170
                        ],
×
171
                ];
×
172

173
                /**
174
                 * Filter: 'wpseo_indexing_data' Filter to adapt the data used in the indexing process.
175
                 *
176
                 * @param array $data The indexing data to adapt.
177
                 */
178
                $data = \apply_filters( 'wpseo_indexing_data', $data );
×
179

180
                $this->admin_asset_manager->localize_script( 'indexation', 'yoastIndexingData', $data );
×
181

182
                $person_id       = $this->get_person_id();
×
183
                $social_profiles = $this->get_social_profiles();
×
184

185
                // This filter is documented in admin/views/tabs/metas/paper-content/general/knowledge-graph.php.
186
                $knowledge_graph_message = \apply_filters( 'wpseo_knowledge_graph_setting_msg', '' );
×
187

188
                $finished_steps        = $this->get_finished_steps();
×
189
                $options               = $this->get_company_or_person_options();
×
190
                $selected_option_label = '';
×
191
                $filtered_options      = \array_filter(
×
192
                        $options,
×
193
                        function ( $item ) {
×
194
                                return $item['value'] === $this->is_company_or_person();
×
195
                        }
×
196
                );
×
197
                $selected_option       = \reset( $filtered_options );
×
198
                if ( \is_array( $selected_option ) ) {
×
199
                        $selected_option_label = $selected_option['label'];
200
                }
201

202
                $data_ftc = [
×
203
                        'canEditUser'                => $this->can_edit_profile( $person_id ),
×
204
                        'companyOrPerson'            => $this->is_company_or_person(),
×
205
                        'companyOrPersonLabel'       => $selected_option_label,
×
206
                        'companyName'                => $this->get_company_name(),
×
207
                        'fallbackCompanyName'        => $this->get_fallback_company_name( $this->get_company_name() ),
×
208
                        'websiteName'                => $this->get_website_name(),
×
209
                        'fallbackWebsiteName'        => $this->get_fallback_website_name( $this->get_website_name() ),
×
210
                        'companyLogo'                => $this->get_company_logo(),
×
211
                        'companyLogoFallback'        => $this->get_company_fallback_logo( $this->get_company_logo() ),
×
212
                        'companyLogoId'              => $this->get_person_logo_id(),
×
213
                        'finishedSteps'              => $finished_steps,
×
214
                        'personId'                   => (int) $person_id,
×
215
                        'personName'                 => $this->get_person_name(),
×
216
                        'personLogo'                 => $this->get_person_logo(),
×
217
                        'personLogoFallback'         => $this->get_person_fallback_logo( $this->get_person_logo() ),
×
218
                        'personLogoId'               => $this->get_person_logo_id(),
×
219
                        'siteTagline'                => $this->get_site_tagline(),
×
220
                        'socialProfiles'             => [
×
221
                                'facebookUrl'     => $social_profiles['facebook_site'],
×
222
                                'twitterUsername' => $social_profiles['twitter_site'],
×
223
                                'otherSocialUrls' => $social_profiles['other_social_urls'],
×
224
                        ],
×
225
                        'isPremium'                  => $this->product_helper->is_premium(),
×
NEW
226
                        'isWooCommerceActive'        => $this->woocommerce_helper->is_active(),
×
NEW
227
                        'isWooCommerceSeoActive'     => $this->is_wooseo_active(),
×
228
                        'tracking'                   => $this->has_tracking_enabled(),
×
229
                        'isTrackingAllowedMultisite' => $this->is_tracking_enabled_multisite(),
×
230
                        'isMainSite'                 => $this->is_main_site(),
×
231
                        'companyOrPersonOptions'     => $options,
×
232
                        'shouldForceCompany'         => $this->should_force_company(),
×
233
                        'knowledgeGraphMessage'      => $knowledge_graph_message,
×
234
                        'shortlinks'                 => [
×
235
                                'gdpr'                     => $this->shortlinker->build_shortlink( 'https://yoa.st/gdpr-config-workout' ),
×
236
                                'configIndexables'         => $this->shortlinker->build_shortlink( 'https://yoa.st/config-indexables' ),
×
237
                                'configIndexablesBenefits' => $this->shortlinker->build_shortlink( 'https://yoa.st/config-indexables-benefits' ),
×
NEW
238
                                'indexationLearnMore'      => $this->shortlinker->build_shortlink( 'https://yoa.st/ftc-indexation-premium-learn-more' ),
×
NEW
239
                                'reprWoocommerceLearnMore' => $this->shortlinker->build_shortlink( 'https://yoa.st/ftc-representation-wooseo-learn-more' ),
×
NEW
240
                                'reprLocalLearnMore'       => $this->shortlinker->build_shortlink( 'https://yoa.st/ftc-representation-local-learn-more' ),
×
NEW
241
                                'finishLearnMore'          => $this->shortlinker->build_shortlink( 'https://yoa.st/ftc-finish-premium-learn-more' ),
×
242
                        ],
×
243
                ];
×
244

245
                $this->admin_asset_manager->localize_script( 'general-page', 'wpseoFirstTimeConfigurationData', $data_ftc );
×
246
        }
247

248
        /**
249
         * Retrieves a list of the endpoints to use.
250
         *
251
         * @return array The endpoints.
252
         */
253
        protected function get_endpoints() {
254
                $endpoints = [
255
                        'prepare'            => Indexing_Route::FULL_PREPARE_ROUTE,
256
                        'terms'              => Indexing_Route::FULL_TERMS_ROUTE,
257
                        'posts'              => Indexing_Route::FULL_POSTS_ROUTE,
258
                        'archives'           => Indexing_Route::FULL_POST_TYPE_ARCHIVES_ROUTE,
259
                        'general'            => Indexing_Route::FULL_GENERAL_ROUTE,
260
                        'indexablesComplete' => Indexing_Route::FULL_INDEXABLES_COMPLETE_ROUTE,
261
                        'post_link'          => Indexing_Route::FULL_POST_LINKS_INDEXING_ROUTE,
262
                        'term_link'          => Indexing_Route::FULL_TERM_LINKS_INDEXING_ROUTE,
263
                ];
264

265
                $endpoints = \apply_filters( 'wpseo_indexing_endpoints', $endpoints );
266

267
                $endpoints['complete'] = Indexing_Route::FULL_COMPLETE_ROUTE;
268

269
                return $endpoints;
270
        }
271

272
        // ** Private functions ** //
273

274
        /**
275
         * Returns the finished steps array.
276
         *
277
         * @return array An array with the finished steps.
278
         */
279
        private function get_finished_steps() {
280
                return $this->options_helper->get( 'configuration_finished_steps', [] );
281
        }
282

283
        /**
284
         * Returns the entity represented by the site.
285
         *
286
         * @return string The entity represented by the site.
287
         */
288
        private function is_company_or_person() {
289
                return $this->options_helper->get( 'company_or_person', '' );
290
        }
291

292
        /**
293
         * Gets the company name from the option in the database.
294
         *
295
         * @return string The company name.
296
         */
297
        private function get_company_name() {
298
                return $this->options_helper->get( 'company_name', '' );
299
        }
300

301
        /**
302
         * Gets the fallback company name from the option in the database if there is no company name.
303
         *
304
         * @param string $company_name The given company name by the user, default empty string.
305
         *
306
         * @return string|false The company name.
307
         */
308
        private function get_fallback_company_name( $company_name ) {
×
309
                if ( $company_name ) {
×
310
                        return false;
311
                }
312

313
                return \get_bloginfo( 'name' );
314
        }
315

316
        /**
317
         * Gets the website name from the option in the database.
318
         *
319
         * @return string The website name.
320
         */
321
        private function get_website_name() {
322
                return $this->options_helper->get( 'website_name', '' );
323
        }
324

325
        /**
326
         * Gets the fallback website name from the option in the database if there is no website name.
327
         *
328
         * @param string $website_name The given website name by the user, default empty string.
329
         *
330
         * @return string|false The website name.
331
         */
332
        private function get_fallback_website_name( $website_name ) {
×
333
                if ( $website_name ) {
×
334
                        return false;
335
                }
336

337
                return \get_bloginfo( 'name' );
338
        }
339

340
        /**
341
         * Gets the company logo from the option in the database.
342
         *
343
         * @return string The company logo.
344
         */
345
        private function get_company_logo() {
346
                return $this->options_helper->get( 'company_logo', '' );
347
        }
348

349
        /**
350
         * Gets the company logo id from the option in the database.
351
         *
352
         * @return string The company logo id.
353
         */
354
        private function get_company_logo_id() {
355
                return $this->options_helper->get( 'company_logo_id', '' );
356
        }
357

358
        /**
359
         * Gets the company logo url from the option in the database.
360
         *
361
         * @param string $company_logo The given company logo by the user, default empty.
362
         *
363
         * @return string|false The company logo URL.
364
         */
365
        private function get_company_fallback_logo( $company_logo ) {
×
366
                if ( $company_logo ) {
×
367
                        return false;
368
                }
369
                $logo_id = $this->meta_tags_context->fallback_to_site_logo();
370

371
                return \esc_url( \wp_get_attachment_url( $logo_id ) );
372
        }
373

374
        /**
375
         * Gets the person id from the option in the database.
376
         *
377
         * @return int|null The person id, null if empty.
378
         */
379
        private function get_person_id() {
380
                return $this->options_helper->get( 'company_or_person_user_id' );
381
        }
382

383
        /**
384
         * Gets the person id from the option in the database.
385
         *
386
         * @return int|null The person id, null if empty.
387
         */
388
        private function get_person_name() {
×
389
                $user = \get_userdata( $this->get_person_id() );
×
390
                if ( $user instanceof WP_User ) {
×
391
                        return $user->get( 'display_name' );
392
                }
393

394
                return '';
395
        }
396

397
        /**
398
         * Gets the person avatar from the option in the database.
399
         *
400
         * @return string The person logo.
401
         */
402
        private function get_person_logo() {
403
                return $this->options_helper->get( 'person_logo', '' );
404
        }
405

406
        /**
407
         * Gets the person logo url from the option in the database.
408
         *
409
         * @param string $person_logo The given person logo by the user, default empty.
410
         *
411
         * @return string|false The person logo URL.
412
         */
413
        private function get_person_fallback_logo( $person_logo ) {
×
414
                if ( $person_logo ) {
×
415
                        return false;
416
                }
417
                $logo_id = $this->meta_tags_context->fallback_to_site_logo();
418

419
                return \esc_url( \wp_get_attachment_url( $logo_id ) );
420
        }
421

422
        /**
423
         * Gets the person logo id from the option in the database.
424
         *
425
         * @return string The person logo id.
426
         */
427
        private function get_person_logo_id() {
428
                return $this->options_helper->get( 'person_logo_id', '' );
429
        }
430

431
        /**
432
         * Gets the site tagline.
433
         *
434
         * @return string The site tagline.
435
         */
436
        private function get_site_tagline() {
437
                return \get_bloginfo( 'description' );
438
        }
439

440
        /**
441
         * Gets the social profiles stored in the database.
442
         *
443
         * @return string[] The social profiles.
444
         */
445
        private function get_social_profiles() {
446
                return $this->social_profiles_helper->get_organization_social_profiles();
447
        }
448

449
        /**
450
         * Checks whether tracking is enabled.
451
         *
452
         * @return bool True if tracking is enabled, false otherwise, null if in Free and conf. workout step not finished.
453
         */
454
        private function has_tracking_enabled() {
×
455
                $default = false;
456

457
                if ( $this->product_helper->is_premium() ) {
×
458
                        $default = true;
459
                }
460

461
                return $this->options_helper->get( 'tracking', $default );
462
        }
463

464
        /**
465
         * Checks whether tracking option is allowed at network level.
466
         *
467
         * @return bool True if option change is allowed, false otherwise.
468
         */
469
        private function is_tracking_enabled_multisite() {
×
470
                $default = true;
471

472
                if ( ! \is_multisite() ) {
×
473
                        return $default;
474
                }
475

476
                return $this->options_helper->get( 'allow_tracking', $default );
477
        }
478

479
        /**
480
         * Checks whether we are in a main site.
481
         *
482
         * @return bool True if it's the main site or a single site, false if it's a subsite.
483
         */
484
        private function is_main_site() {
485
                return \is_main_site();
486
        }
487

488
        /**
489
         * Gets the options for the Company or Person select.
490
         * Returns only the company option if it is forced (by Local SEO), otherwise returns company and person option.
491
         *
492
         * @return array The options for the company-or-person select.
493
         */
494
        private function get_company_or_person_options() {
×
495
                $options = [
×
496
                        [
×
497
                                'label' => \__( 'Organization', 'wordpress-seo' ),
×
498
                                'value' => 'company',
×
499
                                'id'    => 'company',
×
500
                        ],
×
501
                        [
×
502
                                'label' => \__( 'Person', 'wordpress-seo' ),
×
503
                                'value' => 'person',
×
504
                                'id'    => 'person',
×
505
                        ],
×
506
                ];
×
507
                if ( $this->should_force_company() ) {
508
                        $options = [
×
509
                                [
×
510
                                        'label' => \__( 'Organization', 'wordpress-seo' ),
×
511
                                        'value' => 'company',
×
512
                                        'id'    => 'company',
×
513
                                ],
×
514
                        ];
×
515
                }
516

517
                return $options;
518
        }
519

520
        /**
521
         * Checks whether we should force "Organization".
522
         *
523
         * @return bool
524
         */
525
        private function should_force_company() {
526
                return $this->addon_manager->is_installed( WPSEO_Addon_Manager::LOCAL_SLUG );
527
        }
528

529
        /**
530
         * Checks if the current user has the capability to edit a specific user.
531
         *
532
         * @param int $person_id The id of the person to edit.
533
         *
534
         * @return bool
535
         */
536
        private function can_edit_profile( $person_id ) {
537
                return \current_user_can( 'edit_user', $person_id );
538
        }
539

540
        /**
541
         * Checks if Yoast WooCommerce SEO is active.
542
         *
543
         * @return bool
544
         */
NEW
545
        private function is_wooseo_active() {
×
NEW
546
                $addon_manager = new WPSEO_Addon_Manager();
×
547
                return $addon_manager->is_installed( WPSEO_Addon_Manager::WOOCOMMERCE_SLUG );
548
        }
549
}
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