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

Yoast / wordpress-seo / 3a107b77fc7aaeaea07df0273cad8eb4c0c863a2

30 Jan 2024 08:08AM UTC coverage: 53.141% (+0.04%) from 53.106%
3a107b77fc7aaeaea07df0273cad8eb4c0c863a2

Pull #21096

github

web-flow
Merge 2eb97856c into f06dbce37
Pull Request #21096: Links `Person or Organization` node to the `ProfilePage` Node.

7564 of 13900 branches covered (0.0%)

Branch coverage included in aggregate %.

1 of 3 new or added lines in 1 file covered. (33.33%)

56 existing lines in 1 file now uncovered.

29036 of 54973 relevant lines covered (52.82%)

40256.12 hits per line

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

28.57
/src/integrations/settings-integration.php
1
<?php
2

3
namespace Yoast\WP\SEO\Integrations;
4

5
use WP_Post;
6
use WP_Post_Type;
7
use WP_Taxonomy;
8
use WP_User;
9
use WPSEO_Admin_Asset_Manager;
10
use WPSEO_Admin_Editor_Specific_Replace_Vars;
11
use WPSEO_Admin_Recommended_Replace_Vars;
12
use WPSEO_Option_Titles;
13
use WPSEO_Options;
14
use WPSEO_Plugin_Availability;
15
use WPSEO_Replace_Vars;
16
use WPSEO_Shortlinker;
17
use WPSEO_Sitemaps_Router;
18
use Yoast\WP\SEO\Conditionals\Settings_Conditional;
19
use Yoast\WP\SEO\Config\Schema_Types;
20
use Yoast\WP\SEO\Content_Type_Visibility\Application\Content_Type_Visibility_Dismiss_Notifications;
21
use Yoast\WP\SEO\Helpers\Current_Page_Helper;
22
use Yoast\WP\SEO\Helpers\Language_Helper;
23
use Yoast\WP\SEO\Helpers\Options_Helper;
24
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
25
use Yoast\WP\SEO\Helpers\Product_Helper;
26
use Yoast\WP\SEO\Helpers\Schema\Article_Helper;
27
use Yoast\WP\SEO\Helpers\Taxonomy_Helper;
28
use Yoast\WP\SEO\Helpers\User_Helper;
29
use Yoast\WP\SEO\Helpers\Woocommerce_Helper;
30
use Yoast\WP\SEO\Promotions\Application\Promotion_Manager;
31

32
/**
33
 * Class Settings_Integration.
34
 */
35
class Settings_Integration implements Integration_Interface {
36

37
        public const PAGE = 'wpseo_page_settings';
38

39
        /**
40
         * Holds the included WordPress options.
41
         *
42
         * @var string[]
43
         */
44
        public const WP_OPTIONS = [ 'blogdescription' ];
45

46
        /**
47
         * Holds the allowed option groups.
48
         *
49
         * @var array
50
         */
51
        public const ALLOWED_OPTION_GROUPS = [ 'wpseo', 'wpseo_titles', 'wpseo_social' ];
52

53
        /**
54
         * Holds the disallowed settings, per option group.
55
         *
56
         * @var array
57
         */
58
        public const DISALLOWED_SETTINGS = [
59
                'wpseo'        => [
60
                        'myyoast-oauth',
61
                        'semrush_tokens',
62
                        'custom_taxonomy_slugs',
63
                        'zapier_subscription',
64
                        'import_cursors',
65
                        'workouts_data',
66
                        'configuration_finished_steps',
67
                        'importing_completed',
68
                        'wincher_tokens',
69
                        'least_readability_ignore_list',
70
                        'least_seo_score_ignore_list',
71
                        'most_linked_ignore_list',
72
                        'least_linked_ignore_list',
73
                        'indexables_page_reading_list',
74
                        'show_new_content_type_notification',
75
                        'new_post_types',
76
                        'new_taxonomies',
77
                ],
78
                'wpseo_titles' => [
79
                        'company_logo_meta',
80
                        'person_logo_meta',
81
                ],
82
        ];
83

84
        /**
85
         * Holds the disabled on multisite settings, per option group.
86
         *
87
         * @var array
88
         */
89
        public const DISABLED_ON_MULTISITE_SETTINGS = [
90
                'wpseo' => [
91
                        'deny_search_crawling',
92
                        'deny_wp_json_crawling',
93
                        'deny_adsbot_crawling',
94
                        'deny_ccbot_crawling',
95
                        'deny_google_extended_crawling',
96
                        'deny_gptbot_crawling',
97
                ],
98
        ];
99

100
        /**
101
         * Holds the WPSEO_Admin_Asset_Manager.
102
         *
103
         * @var WPSEO_Admin_Asset_Manager
104
         */
105
        protected $asset_manager;
106

107
        /**
108
         * Holds the WPSEO_Replace_Vars.
109
         *
110
         * @var WPSEO_Replace_Vars
111
         */
112
        protected $replace_vars;
113

114
        /**
115
         * Holds the Schema_Types.
116
         *
117
         * @var Schema_Types
118
         */
119
        protected $schema_types;
120

121
        /**
122
         * Holds the Current_Page_Helper.
123
         *
124
         * @var Current_Page_Helper
125
         */
126
        protected $current_page_helper;
127

128
        /**
129
         * Holds the Post_Type_Helper.
130
         *
131
         * @var Post_Type_Helper
132
         */
133
        protected $post_type_helper;
134

135
        /**
136
         * Holds the Language_Helper.
137
         *
138
         * @var Language_Helper
139
         */
140
        protected $language_helper;
141

142
        /**
143
         * Holds the Taxonomy_Helper.
144
         *
145
         * @var Taxonomy_Helper
146
         */
147
        protected $taxonomy_helper;
148

149
        /**
150
         * Holds the Product_Helper.
151
         *
152
         * @var Product_Helper
153
         */
154
        protected $product_helper;
155

156
        /**
157
         * Holds the Woocommerce_Helper.
158
         *
159
         * @var Woocommerce_Helper
160
         */
161
        protected $woocommerce_helper;
162

163
        /**
164
         * Holds the Article_Helper.
165
         *
166
         * @var Article_Helper
167
         */
168
        protected $article_helper;
169

170
        /**
171
         * Holds the User_Helper.
172
         *
173
         * @var User_Helper
174
         */
175
        protected $user_helper;
176

177
        /**
178
         * Holds the Options_Helper instance.
179
         *
180
         * @var Options_Helper
181
         */
182
        protected $options;
183

184
        /**
185
         * Holds the Content_Type_Visibility_Dismiss_Notifications instance.
186
         *
187
         * @var Content_Type_Visibility_Dismiss_Notifications
188
         */
189
        protected $content_type_visibility;
190

191
        /**
192
         * Constructs Settings_Integration.
193
         *
194
         * @param WPSEO_Admin_Asset_Manager                     $asset_manager           The WPSEO_Admin_Asset_Manager.
195
         * @param WPSEO_Replace_Vars                            $replace_vars            The WPSEO_Replace_Vars.
196
         * @param Schema_Types                                  $schema_types            The Schema_Types.
197
         * @param Current_Page_Helper                           $current_page_helper     The Current_Page_Helper.
198
         * @param Post_Type_Helper                              $post_type_helper        The Post_Type_Helper.
199
         * @param Language_Helper                               $language_helper         The Language_Helper.
200
         * @param Taxonomy_Helper                               $taxonomy_helper         The Taxonomy_Helper.
201
         * @param Product_Helper                                $product_helper          The Product_Helper.
202
         * @param Woocommerce_Helper                            $woocommerce_helper      The Woocommerce_Helper.
203
         * @param Article_Helper                                $article_helper          The Article_Helper.
204
         * @param User_Helper                                   $user_helper             The User_Helper.
205
         * @param Options_Helper                                $options                 The options helper.
206
         * @param Content_Type_Visibility_Dismiss_Notifications $content_type_visibility The Content_Type_Visibility_Dismiss_Notifications instance.
207
         */
208
        public function __construct(
2✔
209
                WPSEO_Admin_Asset_Manager $asset_manager,
210
                WPSEO_Replace_Vars $replace_vars,
211
                Schema_Types $schema_types,
212
                Current_Page_Helper $current_page_helper,
213
                Post_Type_Helper $post_type_helper,
214
                Language_Helper $language_helper,
215
                Taxonomy_Helper $taxonomy_helper,
216
                Product_Helper $product_helper,
217
                Woocommerce_Helper $woocommerce_helper,
218
                Article_Helper $article_helper,
219
                User_Helper $user_helper,
220
                Options_Helper $options,
221
                Content_Type_Visibility_Dismiss_Notifications $content_type_visibility
222
        ) {
1✔
223
                $this->asset_manager           = $asset_manager;
2✔
224
                $this->replace_vars            = $replace_vars;
2✔
225
                $this->schema_types            = $schema_types;
2✔
226
                $this->current_page_helper     = $current_page_helper;
2✔
227
                $this->taxonomy_helper         = $taxonomy_helper;
2✔
228
                $this->post_type_helper        = $post_type_helper;
2✔
229
                $this->language_helper         = $language_helper;
2✔
230
                $this->product_helper          = $product_helper;
2✔
231
                $this->woocommerce_helper      = $woocommerce_helper;
2✔
232
                $this->article_helper          = $article_helper;
2✔
233
                $this->user_helper             = $user_helper;
2✔
234
                $this->options                 = $options;
2✔
235
                $this->content_type_visibility = $content_type_visibility;
2✔
236
        }
1✔
237

238
        /**
239
         * Returns the conditionals based on which this loadable should be active.
240
         *
241
         * @return array
242
         */
243
        public static function get_conditionals() {
2✔
244
                return [ Settings_Conditional::class ];
2✔
245
        }
246

247
        /**
248
         * Initializes the integration.
249
         *
250
         * This is the place to register hooks and filters.
251
         *
252
         * @return void
253
         */
254
        public function register_hooks() {
×
255
                // Add page.
256
                \add_filter( 'wpseo_submenu_pages', [ $this, 'add_page' ] );
×
257
                \add_filter( 'admin_menu', [ $this, 'add_settings_saved_page' ] );
×
258

259
                // Are we saving the settings?
260
                if ( $this->current_page_helper->get_current_admin_page() === 'options.php' ) {
×
261
                        // phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged -- This deprecation will be addressed later.
262
                        $post_action = \filter_input( \INPUT_POST, 'action', @\FILTER_SANITIZE_STRING );
×
263
                        $option_page = \filter_input( \INPUT_POST, 'option_page', @\FILTER_SANITIZE_STRING );
×
264
                        // phpcs:enable
265

266
                        if ( $post_action === 'update' && $option_page === self::PAGE ) {
×
267
                                \add_action( 'admin_init', [ $this, 'register_setting' ] );
×
268
                                \add_action( 'in_admin_header', [ $this, 'remove_notices' ], \PHP_INT_MAX );
×
269
                        }
270

271
                        return;
×
272
                }
273

274
                // Are we on the settings page?
275
                if ( $this->current_page_helper->get_current_yoast_seo_page() === self::PAGE ) {
×
276
                        \add_action( 'admin_init', [ $this, 'register_setting' ] );
×
277
                        \add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
×
278
                        \add_action( 'in_admin_header', [ $this, 'remove_notices' ], \PHP_INT_MAX );
×
279
                }
280
        }
281

282
        /**
283
         * Registers the different options under the setting.
284
         *
285
         * @return void
286
         */
287
        public function register_setting() {
×
288
                foreach ( WPSEO_Options::$options as $name => $instance ) {
×
289
                        if ( \in_array( $name, self::ALLOWED_OPTION_GROUPS, true ) ) {
×
290
                                \register_setting( self::PAGE, $name );
×
291
                        }
292
                }
293
                // Only register WP options when the user is allowed to manage them.
294
                if ( \current_user_can( 'manage_options' ) ) {
×
295
                        foreach ( self::WP_OPTIONS as $name ) {
×
296
                                \register_setting( self::PAGE, $name );
×
297
                        }
298
                }
299
        }
300

301
        /**
302
         * Adds the page.
303
         *
304
         * @param array $pages The pages.
305
         *
306
         * @return array The pages.
307
         */
308
        public function add_page( $pages ) {
×
309
                \array_splice(
×
310
                        $pages,
×
311
                        1,
×
312
                        0,
×
313
                        [
314
                                [
315
                                        'wpseo_dashboard',
×
316
                                        '',
×
317
                                        \__( 'Settings', 'wordpress-seo' ),
×
318
                                        'wpseo_manage_options',
×
319
                                        self::PAGE,
×
320
                                        [ $this, 'display_page' ],
×
321
                                ],
322
                        ]
323
                );
324

325
                return $pages;
×
326
        }
327

328
        /**
329
         * Adds a dummy page.
330
         *
331
         * Because the options route NEEDS to redirect to something.
332
         *
333
         * @param array $pages The pages.
334
         *
335
         * @return array The pages.
336
         */
337
        public function add_settings_saved_page( $pages ) {
2✔
338
                \add_submenu_page(
2✔
339
                        '',
2✔
340
                        '',
2✔
341
                        '',
2✔
342
                        'wpseo_manage_options',
2✔
343
                        self::PAGE . '_saved',
2✔
344
                        static function () {
1✔
345
                                // Add success indication to HTML response.
346
                                $success = empty( \get_settings_errors() ) ? 'true' : 'false';
×
347
                                echo \esc_html( "{{ yoast-success: $success }}" );
×
348
                        }
2✔
349
                );
1✔
350

351
                return $pages;
2✔
352
        }
353

354
        /**
355
         * Displays the page.
356
         *
357
         * @return void
358
         */
359
        public function display_page() {
×
360
                echo '<div id="yoast-seo-settings"></div>';
×
361
        }
362

363
        /**
364
         * Enqueues the assets.
365
         *
366
         * @return void
367
         */
368
        public function enqueue_assets() {
×
369
                // Remove the emoji script as it is incompatible with both React and any contenteditable fields.
370
                \remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
×
371
                \wp_enqueue_media();
×
372
                $this->asset_manager->enqueue_script( 'new-settings' );
×
373
                $this->asset_manager->enqueue_style( 'new-settings' );
×
374
                if ( \YoastSEO()->classes->get( Promotion_Manager::class )->is( 'black-friday-2023-promotion' ) ) {
×
375
                        $this->asset_manager->enqueue_style( 'black-friday-banner' );
×
376
                }
377
                $this->asset_manager->localize_script( 'new-settings', 'wpseoScriptData', $this->get_script_data() );
×
378
        }
379

380
        /**
381
         * Removes all current WP notices.
382
         *
383
         * @return void
384
         */
385
        public function remove_notices() {
×
386
                \remove_all_actions( 'admin_notices' );
×
387
                \remove_all_actions( 'user_admin_notices' );
×
388
                \remove_all_actions( 'network_admin_notices' );
×
389
                \remove_all_actions( 'all_admin_notices' );
×
390
        }
391

392
        /**
393
         * Creates the script data.
394
         *
395
         * @return array The script data.
396
         */
397
        protected function get_script_data() {
×
398
                $default_setting_values = $this->get_default_setting_values();
×
399
                $settings               = $this->get_settings( $default_setting_values );
×
400
                $post_types             = $this->post_type_helper->get_indexable_post_type_objects();
×
401
                $taxonomies             = $this->taxonomy_helper->get_indexable_taxonomy_objects();
×
402

403
                // Check if attachments are included in indexation.
404
                if ( ! \array_key_exists( 'attachment', $post_types ) ) {
×
405
                        // Always include attachments in the settings, to let the user enable them again.
406
                        $attachment_object = \get_post_type_object( 'attachment' );
×
407
                        if ( ! empty( $attachment_object ) ) {
×
408
                                $post_types['attachment'] = $attachment_object;
×
409
                        }
410
                }
411
                // Check if post formats are included in indexation.
412
                if ( ! \array_key_exists( 'post_format', $taxonomies ) ) {
×
413
                        // Always include post_format in the settings, to let the user enable them again.
414
                        $post_format_object = \get_taxonomy( 'post_format' );
×
415
                        if ( ! empty( $post_format_object ) ) {
×
416
                                $taxonomies['post_format'] = $post_format_object;
×
417
                        }
418
                }
419

420
                $transformed_post_types = $this->transform_post_types( $post_types );
×
421
                $transformed_taxonomies = $this->transform_taxonomies( $taxonomies, \array_keys( $transformed_post_types ) );
×
422

423
                // Check if there is a new content type to show notification only once in the settings.
424
                $show_new_content_type_notification = $this->content_type_visibility->maybe_add_settings_notification();
×
425

426
                return [
427
                        'settings'                       => $this->transform_settings( $settings ),
×
428
                        'defaultSettingValues'           => $default_setting_values,
×
429
                        'disabledSettings'               => $this->get_disabled_settings( $settings ),
×
430
                        'endpoint'                       => \admin_url( 'options.php' ),
×
431
                        'nonce'                          => \wp_create_nonce( self::PAGE . '-options' ),
×
432
                        'separators'                     => WPSEO_Option_Titles::get_instance()->get_separator_options_for_display(),
×
433
                        'replacementVariables'           => $this->get_replacement_variables(),
×
434
                        'schema'                         => $this->get_schema( $transformed_post_types ),
×
435
                        'preferences'                    => $this->get_preferences( $settings ),
×
436
                        'linkParams'                     => WPSEO_Shortlinker::get_query_params(),
×
437
                        'postTypes'                      => $transformed_post_types,
×
438
                        'taxonomies'                     => $transformed_taxonomies,
×
439
                        'fallbacks'                      => $this->get_fallbacks(),
×
440
                        'showNewContentTypeNotification' => $show_new_content_type_notification,
×
441
                ];
442
        }
443

444
        /**
445
         * Retrieves the preferences.
446
         *
447
         * @param array $settings The settings.
448
         *
449
         * @return array The preferences.
450
         */
451
        protected function get_preferences( $settings ) {
×
452
                $shop_page_id             = $this->woocommerce_helper->get_shop_page_id();
×
453
                $homepage_is_latest_posts = \get_option( 'show_on_front' ) === 'posts';
×
454
                $page_on_front            = \get_option( 'page_on_front' );
×
455
                $page_for_posts           = \get_option( 'page_for_posts' );
×
456

457
                $wpseo_plugin_availability_checker = new WPSEO_Plugin_Availability();
×
458
                $woocommerce_seo_file              = 'wpseo-woocommerce/wpseo-woocommerce.php';
×
459
                $woocommerce_seo_active            = $wpseo_plugin_availability_checker->is_active( $woocommerce_seo_file );
×
460

461
                if ( empty( $page_on_front ) ) {
×
462
                        $page_on_front = $page_for_posts;
×
463
                }
464

465
                $business_settings_url = \get_admin_url( null, 'admin.php?page=wpseo_local' );
×
466
                if ( \defined( 'WPSEO_LOCAL_FILE' ) ) {
×
467
                        $local_options      = \get_option( 'wpseo_local' );
×
468
                        $multiple_locations = $local_options['use_multiple_locations'];
×
469
                        $same_organization  = $local_options['multiple_locations_same_organization'];
×
470
                        if ( $multiple_locations === 'on' && $same_organization !== 'on' ) {
×
471
                                $business_settings_url = \get_admin_url( null, 'edit.php?post_type=wpseo_locations' );
×
472
                        }
473
                }
474

475
                return [
476
                        'isPremium'                     => $this->product_helper->is_premium(),
×
477
                        'isRtl'                         => \is_rtl(),
×
478
                        'isNetworkAdmin'                => \is_network_admin(),
×
479
                        'isMainSite'                    => \is_main_site(),
×
480
                        'isMultisite'                   => \is_multisite(),
×
481
                        'isWooCommerceActive'           => $this->woocommerce_helper->is_active(),
×
482
                        'isLocalSeoActive'              => \defined( 'WPSEO_LOCAL_FILE' ),
×
483
                        'isNewsSeoActive'               => \defined( 'WPSEO_NEWS_FILE' ),
×
484
                        'isWooCommerceSEOActive'        => $woocommerce_seo_active,
×
485
                        'promotions'                    => \YoastSEO()->classes->get( Promotion_Manager::class )->get_current_promotions(),
×
486
                        'siteUrl'                       => \get_bloginfo( 'url' ),
×
487
                        'siteTitle'                     => \get_bloginfo( 'name' ),
×
488
                        'sitemapUrl'                    => WPSEO_Sitemaps_Router::get_base_url( 'sitemap_index.xml' ),
×
489
                        'hasWooCommerceShopPage'        => $shop_page_id !== -1,
490
                        'editWooCommerceShopPageUrl'    => \get_edit_post_link( $shop_page_id, 'js' ),
×
491
                        'wooCommerceShopPageSettingUrl' => \get_admin_url( null, 'admin.php?page=wc-settings&tab=products' ),
×
492
                        'localSeoPageSettingUrl'        => $business_settings_url,
×
493
                        'homepageIsLatestPosts'         => $homepage_is_latest_posts,
×
494
                        'homepagePageEditUrl'           => \get_edit_post_link( $page_on_front, 'js' ),
×
495
                        'homepagePostsEditUrl'          => \get_edit_post_link( $page_for_posts, 'js' ),
×
496
                        'createUserUrl'                 => \admin_url( 'user-new.php' ),
×
497
                        'createPageUrl'                 => \admin_url( 'post-new.php?post_type=page' ),
×
498
                        'editUserUrl'                   => \admin_url( 'user-edit.php' ),
×
499
                        'editTaxonomyUrl'               => \admin_url( 'edit-tags.php' ),
×
500
                        'generalSettingsUrl'            => \admin_url( 'options-general.php' ),
×
501
                        'companyOrPersonMessage'        => \apply_filters( 'wpseo_knowledge_graph_setting_msg', '' ),
×
502
                        'currentUserId'                 => \get_current_user_id(),
×
503
                        'canCreateUsers'                => \current_user_can( 'create_users' ),
×
504
                        'canCreatePages'                => \current_user_can( 'edit_pages' ),
×
505
                        'canEditUsers'                  => \current_user_can( 'edit_users' ),
×
506
                        'canManageOptions'              => \current_user_can( 'manage_options' ),
×
507
                        'userLocale'                    => \str_replace( '_', '-', \get_user_locale() ),
×
508
                        'pluginUrl'                     => \plugins_url( '', \WPSEO_FILE ),
×
509
                        'showForceRewriteTitlesSetting' => ! \current_theme_supports( 'title-tag' ) && ! ( \function_exists( 'wp_is_block_theme' ) && \wp_is_block_theme() ),
×
510
                        'upsellSettings'                => $this->get_upsell_settings(),
×
511
                        'siteRepresentsPerson'          => $this->get_site_represents_person( $settings ),
×
512
                        'siteBasicsPolicies'            => $this->get_site_basics_policies( $settings ),
×
513
                ];
514
        }
515

516
        /**
517
         * Retrieves the currently represented person.
518
         *
519
         * @param array $settings The settings.
520
         *
521
         * @return array The currently represented person's ID and name.
522
         */
523
        protected function get_site_represents_person( $settings ) {
×
524
                $person = [
525
                        'id'   => false,
×
526
                        'name' => '',
527
                ];
528

529
                if ( isset( $settings['wpseo_titles']['company_or_person_user_id'] ) ) {
×
530
                        $person['id'] = $settings['wpseo_titles']['company_or_person_user_id'];
×
531
                        $user         = \get_userdata( $person['id'] );
×
532
                        if ( $user instanceof WP_User ) {
×
533
                                $person['name'] = $user->get( 'display_name' );
×
534
                        }
535
                }
536

537
                return $person;
×
538
        }
539

540
        /**
541
         * Get site policy data.
542
         *
543
         * @param array $settings The settings.
544
         *
545
         * @return array The policy data.
546
         */
547
        private function get_site_basics_policies( $settings ) {
×
548
                $policies = [];
×
549

550
                $policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['publishing_principles_id'], 'publishing_principles_id' );
×
551
                $policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['ownership_funding_info_id'], 'ownership_funding_info_id' );
×
552
                $policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['actionable_feedback_policy_id'], 'actionable_feedback_policy_id' );
×
553
                $policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['corrections_policy_id'], 'corrections_policy_id' );
×
554
                $policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['ethics_policy_id'], 'ethics_policy_id' );
×
555
                $policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['diversity_policy_id'], 'diversity_policy_id' );
×
556
                $policies = $this->maybe_add_policy( $policies, $settings['wpseo_titles']['diversity_staffing_report_id'], 'diversity_staffing_report_id' );
×
557

558
                return $policies;
×
559
        }
560

561
        /**
562
         * Adds policy data if it is present.
563
         *
564
         * @param array  $policies The existing policy data.
565
         * @param int    $policy   The policy id to check.
566
         * @param string $key      The option key name.
567
         *
568
         * @return array<int,string> The policy data.
569
         */
570
        private function maybe_add_policy( $policies, $policy, $key ) {
×
571
                $policy_array = [
572
                        'id'   => 0,
×
573
                        'name' => \__( 'None', 'wordpress-seo' ),
×
574
                ];
575

576
                if ( isset( $policy ) && \is_int( $policy ) ) {
×
577
                        $policy_array['id'] = $policy;
×
578
                        $post               = \get_post( $policy );
×
579
                        if ( $post instanceof WP_Post ) {
×
580
                                if ( $post->post_status !== 'publish' || $post->post_password !== '' ) {
×
581
                                        return $policies;
×
582
                                }
583
                                $policy_array['name'] = $post->post_title;
×
584
                        }
585
                }
586

587
                $policies[ $key ] = $policy_array;
×
588

589
                return $policies;
×
590
        }
591

592
        /**
593
         * Returns settings for the Call to Buy (CTB) buttons.
594
         *
595
         * @return array<string> The array of CTB settings.
596
         */
597
        public function get_upsell_settings() {
×
598
                return [
599
                        'actionId'     => 'load-nfd-ctb',
×
600
                        'premiumCtbId' => 'f6a84663-465f-4cb5-8ba5-f7a6d72224b2',
601
                ];
602
        }
603

604
        /**
605
         * Retrieves the default setting values.
606
         *
607
         * These default values are currently being used in the UI for dummy fields.
608
         * Dummy fields should not expose or reflect the actual data.
609
         *
610
         * @return array The default setting values.
611
         */
612
        protected function get_default_setting_values() {
×
613
                $defaults = [];
×
614

615
                // Add Yoast settings.
616
                foreach ( WPSEO_Options::$options as $option_name => $instance ) {
×
617
                        if ( \in_array( $option_name, self::ALLOWED_OPTION_GROUPS, true ) ) {
×
618
                                $option_instance          = WPSEO_Options::get_option_instance( $option_name );
×
619
                                $defaults[ $option_name ] = ( $option_instance ) ? $option_instance->get_defaults() : [];
×
620
                        }
621
                }
622
                // Add WP settings.
623
                foreach ( self::WP_OPTIONS as $option_name ) {
×
624
                        $defaults[ $option_name ] = '';
×
625
                }
626

627
                // Remove disallowed settings.
628
                foreach ( self::DISALLOWED_SETTINGS as $option_name => $disallowed_settings ) {
×
629
                        foreach ( $disallowed_settings as $disallowed_setting ) {
×
630
                                unset( $defaults[ $option_name ][ $disallowed_setting ] );
×
631
                        }
632
                }
633

634
                if ( \defined( 'WPSEO_LOCAL_FILE' ) ) {
×
635
                        $defaults = $this->get_defaults_from_local_seo( $defaults );
×
636
                }
637

638
                return $defaults;
×
639
        }
640

641
        /**
642
         * Retrieves the organization schema values from Local SEO for defaults in Site representation fields.
643
         * Specifically for the org-vat-id, org-tax-id, org-email and org-phone options.
644
         *
645
         * @param array<string|int|bool> $defaults The settings defaults.
646
         *
647
         * @return array<string|int|bool> The settings defaults with local seo overides.
648
         */
649
        protected function get_defaults_from_local_seo( $defaults ) {
8✔
650
                $local_options      = \get_option( 'wpseo_local' );
8✔
651
                $multiple_locations = $local_options['use_multiple_locations'];
8✔
652
                $same_organization  = $local_options['multiple_locations_same_organization'];
8✔
653
                $shared_info        = $local_options['multiple_locations_shared_business_info'];
8✔
654
                if ( $multiple_locations !== 'on' || ( $multiple_locations === 'on' && $same_organization === 'on' && $shared_info === 'on' ) ) {
8✔
655
                        $defaults['wpseo_titles']['org-vat-id'] = $local_options['location_vat_id'];
6✔
656
                        $defaults['wpseo_titles']['org-tax-id'] = $local_options['location_tax_id'];
6✔
657
                        $defaults['wpseo_titles']['org-email']  = $local_options['location_email'];
6✔
658
                        $defaults['wpseo_titles']['org-phone']  = $local_options['location_phone'];
6✔
659
                }
660

661
                if ( \wpseo_has_primary_location() ) {
8✔
662
                        $primary_location = $local_options['multiple_locations_primary_location'];
4✔
663

664
                        $location_keys = [
2✔
665
                                'org-phone'  => [
4✔
666
                                        'is_overridden' => '_wpseo_is_overridden_business_phone',
2✔
667
                                        'value'         => '_wpseo_business_phone',
2✔
668
                                ],
2✔
669
                                'org-email'  => [
2✔
670
                                        'is_overridden' => '_wpseo_is_overridden_business_email',
2✔
671
                                        'value'         => '_wpseo_business_email',
2✔
672
                                ],
2✔
673
                                'org-tax-id' => [
2✔
674
                                        'is_overridden' => '_wpseo_is_overridden_business_tax_id',
2✔
675
                                        'value'         => '_wpseo_business_tax_id',
2✔
676
                                ],
2✔
677
                                'org-vat-id' => [
2✔
678
                                        'is_overridden' => '_wpseo_is_overridden_business_vat_id',
2✔
679
                                        'value'         => '_wpseo_business_vat_id',
2✔
680
                                ],
2✔
681
                        ];
2✔
682

683
                        foreach ( $location_keys as $key => $meta_keys ) {
4✔
684
                                $is_overridden = ( $shared_info === 'on' ) ? \get_post_meta( $primary_location, $meta_keys['is_overridden'], true ) : false;
4✔
685
                                if ( $is_overridden === 'on' || $shared_info !== 'on' ) {
4✔
686
                                        $post_meta_value                  = \get_post_meta( $primary_location, $meta_keys['value'], true );
4✔
687
                                        $defaults['wpseo_titles'][ $key ] = ( $post_meta_value ) ? $post_meta_value : '';
4✔
688
                                }
689
                        }
690
                }
691

692
                return $defaults;
8✔
693
        }
694

695
        /**
696
         * Retrieves the settings and their values.
697
         *
698
         * @param array $default_setting_values The default setting values.
699
         *
700
         * @return array The settings.
701
         */
702
        protected function get_settings( $default_setting_values ) {
×
703
                $settings = [];
×
704

705
                // Add Yoast settings.
UNCOV
706
                foreach ( WPSEO_Options::$options as $option_name => $instance ) {
×
UNCOV
707
                        if ( \in_array( $option_name, self::ALLOWED_OPTION_GROUPS, true ) ) {
×
708
                                $settings[ $option_name ] = \array_merge( $default_setting_values[ $option_name ], WPSEO_Options::get_option( $option_name ) );
×
709
                        }
710
                }
711
                // Add WP settings.
UNCOV
712
                foreach ( self::WP_OPTIONS as $option_name ) {
×
UNCOV
713
                        $settings[ $option_name ] = \get_option( $option_name );
×
714
                }
715

716
                // Remove disallowed settings.
UNCOV
717
                foreach ( self::DISALLOWED_SETTINGS as $option_name => $disallowed_settings ) {
×
718
                        foreach ( $disallowed_settings as $disallowed_setting ) {
×
719
                                unset( $settings[ $option_name ][ $disallowed_setting ] );
×
720
                        }
721
                }
722

UNCOV
723
                return $settings;
×
724
        }
725

726
        /**
727
         * Transforms setting values.
728
         *
729
         * @param array $settings The settings.
730
         *
731
         * @return array The settings.
732
         */
UNCOV
733
        protected function transform_settings( $settings ) {
×
734
                if ( isset( $settings['wpseo_titles']['breadcrumbs-sep'] ) ) {
×
735
                        /**
736
                         * The breadcrumbs separator default value is the HTML entity `&raquo;`.
737
                         * Which does not get decoded in our JS, while it did in our Yoast form. Decode it here as an exception.
738
                         */
UNCOV
739
                        $settings['wpseo_titles']['breadcrumbs-sep'] = \html_entity_decode(
×
740
                                $settings['wpseo_titles']['breadcrumbs-sep'],
×
UNCOV
741
                                ( \ENT_NOQUOTES | \ENT_HTML5 ),
×
UNCOV
742
                                'UTF-8'
×
743
                        );
744
                }
745

746
                /**
747
                 * Decode some WP options.
748
                 */
UNCOV
749
                $settings['blogdescription'] = \html_entity_decode(
×
750
                        $settings['blogdescription'],
×
751
                        ( \ENT_NOQUOTES | \ENT_HTML5 ),
×
752
                        'UTF-8'
×
753
                );
754

755
                return $settings;
×
756
        }
757

758
        /**
759
         * Retrieves the disabled settings.
760
         *
761
         * @param array $settings The settings.
762
         *
763
         * @return array The settings.
764
         */
765
        protected function get_disabled_settings( $settings ) {
×
766
                $disabled_settings = [];
×
UNCOV
767
                $site_language     = $this->language_helper->get_language();
×
768

UNCOV
769
                foreach ( WPSEO_Options::$options as $option_name => $instance ) {
×
UNCOV
770
                        if ( ! \in_array( $option_name, self::ALLOWED_OPTION_GROUPS, true ) ) {
×
UNCOV
771
                                continue;
×
772
                        }
773

774
                        $disabled_settings[ $option_name ] = [];
×
775
                        $option_instance                   = WPSEO_Options::get_option_instance( $option_name );
×
776
                        if ( $option_instance === false ) {
×
UNCOV
777
                                continue;
×
778
                        }
UNCOV
779
                        foreach ( $settings[ $option_name ] as $setting_name => $setting_value ) {
×
UNCOV
780
                                if ( $option_instance->is_disabled( $setting_name ) ) {
×
UNCOV
781
                                        $disabled_settings[ $option_name ][ $setting_name ] = 'network';
×
782
                                }
783
                        }
784
                }
785

786
                // Remove disabled on multisite settings.
UNCOV
787
                if ( \is_multisite() ) {
×
UNCOV
788
                        foreach ( self::DISABLED_ON_MULTISITE_SETTINGS as $option_name => $disabled_ms_settings ) {
×
UNCOV
789
                                if ( \array_key_exists( $option_name, $disabled_settings ) ) {
×
UNCOV
790
                                        foreach ( $disabled_ms_settings as $disabled_ms_setting ) {
×
UNCOV
791
                                                $disabled_settings[ $option_name ][ $disabled_ms_setting ] = 'multisite';
×
792
                                        }
793
                                }
794
                        }
795
                }
796

797
                if ( \array_key_exists( 'wpseo', $disabled_settings ) && ! $this->language_helper->has_inclusive_language_support( $site_language ) ) {
×
UNCOV
798
                        $disabled_settings['wpseo']['inclusive_language_analysis_active'] = 'language';
×
799
                }
800

801
                return $disabled_settings;
×
802
        }
803

804
        /**
805
         * Retrieves the replacement variables.
806
         *
807
         * @return array The replacement variables.
808
         */
UNCOV
809
        protected function get_replacement_variables() {
×
UNCOV
810
                $recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
×
UNCOV
811
                $specific_replace_vars    = new WPSEO_Admin_Editor_Specific_Replace_Vars();
×
UNCOV
812
                $replacement_variables    = $this->replace_vars->get_replacement_variables_with_labels();
×
813

814
                return [
815
                        'variables'   => $replacement_variables,
×
UNCOV
816
                        'recommended' => $recommended_replace_vars->get_recommended_replacevars(),
×
817
                        'specific'    => $specific_replace_vars->get(),
×
818
                        'shared'      => $specific_replace_vars->get_generic( $replacement_variables ),
×
819
                ];
820
        }
821

822
        /**
823
         * Retrieves the schema.
824
         *
825
         * @param array $post_types The post types.
826
         *
827
         * @return array The schema.
828
         */
UNCOV
829
        protected function get_schema( array $post_types ) {
×
UNCOV
830
                $schema = [];
×
831

832
                foreach ( $this->schema_types->get_article_type_options() as $article_type ) {
×
833
                        $schema['articleTypes'][ $article_type['value'] ] = [
×
834
                                'label' => $article_type['name'],
×
835
                                'value' => $article_type['value'],
×
836
                        ];
837
                }
838

UNCOV
839
                foreach ( $this->schema_types->get_page_type_options() as $page_type ) {
×
UNCOV
840
                        $schema['pageTypes'][ $page_type['value'] ] = [
×
UNCOV
841
                                'label' => $page_type['name'],
×
UNCOV
842
                                'value' => $page_type['value'],
×
843
                        ];
844
                }
845

UNCOV
846
                $schema['articleTypeDefaults'] = [];
×
UNCOV
847
                $schema['pageTypeDefaults']    = [];
×
UNCOV
848
                foreach ( $post_types as $name => $post_type ) {
×
UNCOV
849
                        $schema['articleTypeDefaults'][ $name ] = WPSEO_Options::get_default( 'wpseo_titles', "schema-article-type-$name" );
×
UNCOV
850
                        $schema['pageTypeDefaults'][ $name ]    = WPSEO_Options::get_default( 'wpseo_titles', "schema-page-type-$name" );
×
851
                }
852

UNCOV
853
                return $schema;
×
854
        }
855

856
        /**
857
         * Transforms the post types, to represent them.
858
         *
859
         * @param WP_Post_Type[] $post_types The WP_Post_Type array to transform.
860
         *
861
         * @return array The post types.
862
         */
863
        protected function transform_post_types( $post_types ) {
4✔
864
                $transformed    = [];
4✔
865
                $new_post_types = $this->options->get( 'new_post_types', [] );
4✔
866
                foreach ( $post_types as $post_type ) {
4✔
867
                        $transformed[ $post_type->name ] = [
4✔
868
                                'name'                 => $post_type->name,
4✔
869
                                'route'                => $this->get_route( $post_type->name, $post_type->rewrite, $post_type->rest_base ),
4✔
870
                                'label'                => $post_type->label,
4✔
871
                                'singularLabel'        => $post_type->labels->singular_name,
4✔
872
                                'hasArchive'           => $this->post_type_helper->has_archive( $post_type ),
4✔
873
                                'hasSchemaArticleType' => $this->article_helper->is_article_post_type( $post_type->name ),
4✔
874
                                'menuPosition'         => $post_type->menu_position,
4✔
875
                                'isNew'                => \in_array( $post_type->name, $new_post_types, true ),
4✔
876
                        ];
2✔
877
                }
878

879
                \uasort( $transformed, [ $this, 'compare_post_types' ] );
4✔
880

881
                return $transformed;
4✔
882
        }
883

884
        /**
885
         * Compares two post types.
886
         *
887
         * @param array $a The first post type.
888
         * @param array $b The second post type.
889
         *
890
         * @return int The order.
891
         */
UNCOV
892
        protected function compare_post_types( $a, $b ) {
×
UNCOV
893
                if ( $a['menuPosition'] === null && $b['menuPosition'] !== null ) {
×
UNCOV
894
                        return 1;
×
895
                }
UNCOV
896
                if ( $a['menuPosition'] !== null && $b['menuPosition'] === null ) {
×
UNCOV
897
                        return -1;
×
898
                }
899

UNCOV
900
                if ( $a['menuPosition'] === null && $b['menuPosition'] === null ) {
×
901
                        // No position specified, order alphabetically by label.
UNCOV
902
                        return \strnatcmp( $a['label'], $b['label'] );
×
903
                }
904

UNCOV
905
                return ( ( $a['menuPosition'] < $b['menuPosition'] ) ? -1 : 1 );
×
906
        }
907

908
        /**
909
         * Transforms the taxonomies, to represent them.
910
         *
911
         * @param WP_Taxonomy[] $taxonomies      The WP_Taxonomy array to transform.
912
         * @param string[]      $post_type_names The post type names.
913
         *
914
         * @return array The taxonomies.
915
         */
916
        protected function transform_taxonomies( $taxonomies, $post_type_names ) {
4✔
917
                $transformed    = [];
4✔
918
                $new_taxonomies = $this->options->get( 'new_taxonomies', [] );
4✔
919
                foreach ( $taxonomies as $taxonomy ) {
4✔
920
                        $transformed[ $taxonomy->name ] = [
4✔
921
                                'name'          => $taxonomy->name,
4✔
922
                                'route'         => $this->get_route( $taxonomy->name, $taxonomy->rewrite, $taxonomy->rest_base ),
4✔
923
                                'label'         => $taxonomy->label,
4✔
924
                                'showUi'        => $taxonomy->show_ui,
4✔
925
                                'singularLabel' => $taxonomy->labels->singular_name,
4✔
926
                                'postTypes'     => \array_filter(
4✔
927
                                        $taxonomy->object_type,
4✔
928
                                        static function ( $object_type ) use ( $post_type_names ) {
2✔
929
                                                return \in_array( $object_type, $post_type_names, true );
4✔
930
                                        }
2✔
931
                                ),
4✔
932
                                'isNew'         => \in_array( $taxonomy->name, $new_taxonomies, true ),
2✔
933
                        ];
2✔
934
                }
935

936
                \uasort(
4✔
937
                        $transformed,
2✔
938
                        static function ( $a, $b ) {
2✔
939
                                return \strnatcmp( $a['label'], $b['label'] );
940
                        }
2✔
941
                );
4✔
942

943
                return $transformed;
944
        }
945

946
        /**
947
         * Gets the route from a name, rewrite and rest_base.
948
         *
949
         * @param string $name      The name.
950
         * @param array  $rewrite   The rewrite data.
951
         * @param string $rest_base The rest base.
952
         *
953
         * @return string The route.
954
         */
UNCOV
955
        protected function get_route( $name, $rewrite, $rest_base ) {
×
UNCOV
956
                $route = $name;
×
957
                if ( isset( $rewrite['slug'] ) ) {
UNCOV
958
                        $route = $rewrite['slug'];
×
959
                }
960
                if ( ! empty( $rest_base ) ) {
961
                        $route = $rest_base;
962
                }
963
                // Always strip leading slashes.
964
                while ( \substr( $route, 0, 1 ) === '/' ) {
965
                        $route = \substr( $route, 1 );
966
                }
967

968
                return \rawurlencode( $route );
969
        }
970

971
        /**
972
         * Retrieves the fallbacks.
973
         *
974
         * @return array The fallbacks.
975
         */
UNCOV
976
        protected function get_fallbacks() {
×
UNCOV
977
                $site_logo_id = \get_option( 'site_logo' );
×
978
                if ( ! $site_logo_id ) {
UNCOV
979
                        $site_logo_id = \get_theme_mod( 'custom_logo' );
×
980
                }
981
                if ( ! $site_logo_id ) {
982
                        $site_logo_id = '0';
983
                }
984

985
                return [
986
                        'siteLogoId' => $site_logo_id,
987
                ];
988
        }
989
}
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

© 2025 Coveralls, Inc