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

Yoast / wordpress-seo / dd6e866a9e6d253114633104d9e3858d807178ba

19 Jun 2024 10:03AM UTC coverage: 48.628% (-4.3%) from 52.936%
dd6e866a9e6d253114633104d9e3858d807178ba

push

github

web-flow
Merge pull request #21431 from Yoast/21429-update-copy-in-the-introduction-and-consent-modals

Updates the copy for the introduction and consent modals

7441 of 13454 branches covered (55.31%)

Branch coverage included in aggregate %.

0 of 3 new or added lines in 2 files covered. (0.0%)

3718 existing lines in 107 files now uncovered.

25100 of 53464 relevant lines covered (46.95%)

62392.47 hits per line

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

2.58
/admin/metabox/class-metabox.php
1
<?php
2
/**
3
 * WPSEO plugin file.
4
 *
5
 * @package WPSEO\Admin
6
 */
7

8
use Yoast\WP\SEO\Actions\Alert_Dismissal_Action;
9
use Yoast\WP\SEO\Conditionals\Third_Party\Jetpack_Boost_Active_Conditional;
10
use Yoast\WP\SEO\Conditionals\Third_Party\Jetpack_Boost_Not_Premium_Conditional;
11
use Yoast\WP\SEO\Conditionals\WooCommerce_Conditional;
12
use Yoast\WP\SEO\Introductions\Infrastructure\Wistia_Embed_Permission_Repository;
13
use Yoast\WP\SEO\Presenters\Admin\Alert_Presenter;
14
use Yoast\WP\SEO\Presenters\Admin\Meta_Fields_Presenter;
15
use Yoast\WP\SEO\Promotions\Application\Promotion_Manager;
16

17
/**
18
 * This class generates the metabox on the edit post / page as well as contains all page analysis functionality.
19
 */
20
class WPSEO_Metabox extends WPSEO_Meta {
21

22
        /**
23
         * Whether the social tab is enabled.
24
         *
25
         * @var bool
26
         */
27
        private $social_is_enabled;
28

29
        /**
30
         * Helper to determine whether the SEO analysis is enabled.
31
         *
32
         * @var WPSEO_Metabox_Analysis_SEO
33
         */
34
        protected $seo_analysis;
35

36
        /**
37
         * Helper to determine whether the readability analysis is enabled.
38
         *
39
         * @var WPSEO_Metabox_Analysis_Readability
40
         */
41
        protected $readability_analysis;
42

43
        /**
44
         * Helper to determine whether the inclusive language analysis is enabled.
45
         *
46
         * @var WPSEO_Metabox_Analysis_Inclusive_Language
47
         */
48
        protected $inclusive_language_analysis;
49

50
        /**
51
         * The metabox editor object.
52
         *
53
         * @var WPSEO_Metabox_Editor
54
         */
55
        protected $editor;
56

57
        /**
58
         * The Metabox post.
59
         *
60
         * @var WP_Post
61
         */
62
        protected $post = null;
63

64
        /**
65
         * Whether the advanced metadata is enabled.
66
         *
67
         * @var bool
68
         */
69
        protected $is_advanced_metadata_enabled;
70

71
        /**
72
         * Class constructor.
73
         */
74
        public function __construct() {
×
75
                if ( $this->is_internet_explorer() ) {
×
76
                        add_action( 'add_meta_boxes', [ $this, 'internet_explorer_metabox' ] );
×
77

78
                        return;
×
79
                }
80

81
                add_action( 'add_meta_boxes', [ $this, 'add_meta_box' ] );
×
82
                add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
×
83
                add_action( 'wp_insert_post', [ $this, 'save_postdata' ] );
×
84
                add_action( 'edit_attachment', [ $this, 'save_postdata' ] );
×
85
                add_action( 'add_attachment', [ $this, 'save_postdata' ] );
×
86
                add_action( 'admin_init', [ $this, 'translate_meta_boxes' ] );
×
87

88
                $this->editor = new WPSEO_Metabox_Editor();
×
89
                $this->editor->register_hooks();
×
90

91
                $this->social_is_enabled            = WPSEO_Options::get( 'opengraph', false ) || WPSEO_Options::get( 'twitter', false );
×
92
                $this->is_advanced_metadata_enabled = WPSEO_Capability_Utils::current_user_can( 'wpseo_edit_advanced_metadata' ) || WPSEO_Options::get( 'disableadvanced_meta' ) === false;
×
93

94
                $this->seo_analysis                = new WPSEO_Metabox_Analysis_SEO();
×
95
                $this->readability_analysis        = new WPSEO_Metabox_Analysis_Readability();
×
96
                $this->inclusive_language_analysis = new WPSEO_Metabox_Analysis_Inclusive_Language();
×
97
        }
98

99
        /**
100
         * Checks whether the request comes from an IE 11 browser.
101
         *
102
         * @return bool Whether the request comes from an IE 11 browser.
103
         */
104
        public static function is_internet_explorer() {
×
105
                if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
×
106
                        return false;
×
107
                }
108

109
                $user_agent = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
×
110

111
                if ( stripos( $user_agent, 'Trident/7.0' ) === false ) {
×
112
                        return false;
×
113
                }
114

115
                return true;
×
116
        }
117

118
        /**
119
         * Adds an alternative metabox for internet explorer users.
120
         *
121
         * @return void
122
         */
123
        public function internet_explorer_metabox() {
×
124
                $post_types = WPSEO_Post_Type::get_accessible_post_types();
×
125
                $post_types = array_filter( $post_types, [ $this, 'display_metabox' ] );
×
126

127
                if ( ! is_array( $post_types ) || $post_types === [] ) {
×
128
                        return;
×
129
                }
130

131
                $product_title = $this->get_product_title();
×
132

133
                foreach ( $post_types as $post_type ) {
×
134
                        add_filter( "postbox_classes_{$post_type}_wpseo_meta", [ $this, 'wpseo_metabox_class' ] );
×
135

136
                        add_meta_box(
×
137
                                'wpseo_meta',
×
138
                                $product_title,
×
139
                                [ $this, 'render_internet_explorer_notice' ],
×
140
                                $post_type,
×
141
                                'normal',
×
142
                                apply_filters( 'wpseo_metabox_prio', 'high' ),
×
143
                                [ '__block_editor_compatible_meta_box' => true ]
×
144
                        );
145
                }
146
        }
147

148
        /**
149
         * Renders the content for the internet explorer metabox.
150
         *
151
         * @return void
152
         */
153
        public function render_internet_explorer_notice() {
×
154
                $content = sprintf(
×
155
                        /* translators: 1: Link start tag to the Firefox website, 2: Link start tag to the Chrome website, 3: Link start tag to the Edge website, 4: Link closing tag. */
156
                        esc_html__( 'The browser you are currently using is unfortunately rather dated. Since we strive to give you the best experience possible, we no longer support this browser. Instead, please use %1$sFirefox%4$s, %2$sChrome%4$s or %3$sMicrosoft Edge%4$s.', 'wordpress-seo' ),
×
157
                        '<a href="https://www.mozilla.org/firefox/new/">',
×
158
                        '<a href="https://www.google.com/chrome/">',
×
159
                        '<a href="https://www.microsoft.com/windows/microsoft-edge">',
×
160
                        '</a>'
×
161
                );
162

163
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped above.
164
                echo new Alert_Presenter( $content );
×
165
        }
166

167
        /**
168
         * Translates text strings for use in the meta box.
169
         *
170
         * IMPORTANT: if you want to add a new string (option) somewhere, make sure you add that array key to
171
         * the main meta box definition array in the class WPSEO_Meta() as well!!!!
172
         *
173
         * @return void
174
         */
175
        public static function translate_meta_boxes() {
×
176
                WPSEO_Meta::$meta_fields['general']['title']['title']    = __( 'SEO title', 'wordpress-seo' );
×
177
                WPSEO_Meta::$meta_fields['general']['metadesc']['title'] = __( 'Meta description', 'wordpress-seo' );
×
178

179
                /* translators: %s expands to the post type name. */
180
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-noindex']['title'] = __( 'Allow search engines to show this %s in search results?', 'wordpress-seo' );
×
181
                if ( (string) get_option( 'blog_public' ) === '0' ) {
×
182
                        WPSEO_Meta::$meta_fields['advanced']['meta-robots-noindex']['description'] = '<span class="error-message">' . __( 'Warning: even though you can set the meta robots setting here, the entire site is set to noindex in the sitewide privacy settings, so these settings won\'t have an effect.', 'wordpress-seo' ) . '</span>';
×
183
                }
184
                /* translators: %1$s expands to Yes or No,  %2$s expands to the post type name.*/
185
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-noindex']['options']['0'] = __( 'Default for %2$s, currently: %1$s', 'wordpress-seo' );
×
186
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-noindex']['options']['2'] = __( 'Yes', 'wordpress-seo' );
×
187
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-noindex']['options']['1'] = __( 'No', 'wordpress-seo' );
×
188

189
                /* translators: %1$s expands to the post type name.*/
190
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-nofollow']['title']        = __( 'Should search engines follow links on this %1$s?', 'wordpress-seo' );
×
191
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-nofollow']['options']['0'] = __( 'Yes', 'wordpress-seo' );
×
192
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-nofollow']['options']['1'] = __( 'No', 'wordpress-seo' );
×
193

194
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-adv']['title']                   = __( 'Meta robots advanced', 'wordpress-seo' );
×
195
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-adv']['description']             = __( 'If you want to apply advanced <code>meta</code> robots settings for this page, please define them in the following field.', 'wordpress-seo' );
×
196
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-adv']['options']['noimageindex'] = __( 'No Image Index', 'wordpress-seo' );
×
197
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-adv']['options']['noarchive']    = __( 'No Archive', 'wordpress-seo' );
×
198
                WPSEO_Meta::$meta_fields['advanced']['meta-robots-adv']['options']['nosnippet']    = __( 'No Snippet', 'wordpress-seo' );
×
199

200
                WPSEO_Meta::$meta_fields['advanced']['bctitle']['title']       = __( 'Breadcrumbs Title', 'wordpress-seo' );
×
201
                WPSEO_Meta::$meta_fields['advanced']['bctitle']['description'] = __( 'Title to use for this page in breadcrumb paths', 'wordpress-seo' );
×
202

203
                WPSEO_Meta::$meta_fields['advanced']['canonical']['title'] = __( 'Canonical URL', 'wordpress-seo' );
×
204

205
                WPSEO_Meta::$meta_fields['advanced']['canonical']['description'] = sprintf(
×
206
                        /* translators: 1: link open tag; 2: link close tag. */
207
                        __( 'The canonical URL that this page should point to. Leave empty to default to permalink. %1$sCross domain canonical%2$s supported too.', 'wordpress-seo' ),
×
208
                        '<a href="https://googlewebmastercentral.blogspot.com/2009/12/handling-legitimate-cross-domain.html" target="_blank" rel="noopener">',
×
209
                        WPSEO_Admin_Utils::get_new_tab_message() . '</a>'
×
210
                );
211

212
                WPSEO_Meta::$meta_fields['advanced']['redirect']['title']       = __( '301 Redirect', 'wordpress-seo' );
×
213
                WPSEO_Meta::$meta_fields['advanced']['redirect']['description'] = __( 'The URL that this page should redirect to.', 'wordpress-seo' );
×
214

215
                do_action( 'wpseo_tab_translate' );
×
216
        }
217

218
        /**
219
         * Determines whether the metabox should be shown for the passed identifier.
220
         *
221
         * By default the check is done for post types, but can also be used for taxonomies.
222
         *
223
         * @param string|null $identifier The identifier to check.
224
         * @param string      $type       The type of object to check. Defaults to post_type.
225
         *
226
         * @return bool Whether or not the metabox should be displayed.
227
         */
228
        public function display_metabox( $identifier = null, $type = 'post_type' ) {
×
229
                return WPSEO_Utils::is_metabox_active( $identifier, $type );
×
230
        }
231

232
        /**
233
         * Adds the Yoast SEO meta box to the edit boxes in the edit post, page,
234
         * attachment, and custom post types pages.
235
         *
236
         * @return void
237
         */
UNCOV
238
        public function add_meta_box() {
×
UNCOV
239
                $post_types = WPSEO_Post_Type::get_accessible_post_types();
×
UNCOV
240
                $post_types = array_filter( $post_types, [ $this, 'display_metabox' ] );
×
241

UNCOV
242
                if ( ! is_array( $post_types ) || $post_types === [] ) {
×
243
                        return;
×
244
                }
245

UNCOV
246
                $product_title = $this->get_product_title();
×
247

UNCOV
248
                foreach ( $post_types as $post_type ) {
×
UNCOV
249
                        add_filter( "postbox_classes_{$post_type}_wpseo_meta", [ $this, 'wpseo_metabox_class' ] );
×
250

UNCOV
251
                        add_meta_box(
×
UNCOV
252
                                'wpseo_meta',
×
UNCOV
253
                                $product_title,
×
UNCOV
254
                                [ $this, 'meta_box' ],
×
UNCOV
255
                                $post_type,
×
UNCOV
256
                                'normal',
×
UNCOV
257
                                apply_filters( 'wpseo_metabox_prio', 'high' ),
×
UNCOV
258
                                [ '__block_editor_compatible_meta_box' => true ]
×
259
                        );
260
                }
261
        }
262

263
        /**
264
         * Adds CSS classes to the meta box.
265
         *
266
         * @param string[] $classes An array of postbox CSS classes.
267
         *
268
         * @return string[]  List of classes that will be applied to the editbox container.
269
         */
270
        public function wpseo_metabox_class( $classes ) {
×
271
                $classes[] = 'yoast wpseo-metabox';
×
272

273
                return $classes;
×
274
        }
275

276
        /**
277
         * Passes variables to js for use with the post-scraper.
278
         *
279
         * @return array<string,string|array<string|int|bool>|bool|int>
280
         */
281
        public function get_metabox_script_data() {
×
282
                $permalink = '';
×
283

284
                if ( is_object( $this->get_metabox_post() ) ) {
×
285
                        $permalink = get_sample_permalink( $this->get_metabox_post()->ID );
×
286
                        $permalink = $permalink[0];
×
287
                }
288

289
                $post_formatter = new WPSEO_Metabox_Formatter(
×
290
                        new WPSEO_Post_Metabox_Formatter( $this->get_metabox_post(), [], $permalink )
×
291
                );
292

293
                $values = $post_formatter->get_values();
×
294
                /** This filter is documented in admin/filters/class-cornerstone-filter.php. */
295
                $post_types = apply_filters( 'wpseo_cornerstone_post_types', WPSEO_Post_Type::get_accessible_post_types() );
×
296
                if ( $values['cornerstoneActive'] && ! in_array( $this->get_metabox_post()->post_type, $post_types, true ) ) {
×
297
                        $values['cornerstoneActive'] = false;
×
298
                }
299

300
                if ( $values['semrushIntegrationActive'] && $this->post->post_type === 'attachment' ) {
×
301
                        $values['semrushIntegrationActive'] = 0;
×
302
                }
303

304
                if ( $values['wincherIntegrationActive'] && $this->post->post_type === 'attachment' ) {
×
305
                        $values['wincherIntegrationActive'] = 0;
×
306
                }
307

308
                return $values;
×
309
        }
310

311
        /**
312
         * Determines whether or not the current post type has registered taxonomies.
313
         *
314
         * @return bool Whether the current post type has taxonomies.
315
         */
316
        private function current_post_type_has_taxonomies() {
×
317
                $post_taxonomies = get_object_taxonomies( get_post_type() );
×
318

319
                return ! empty( $post_taxonomies );
×
320
        }
321

322
        /**
323
         * Determines the scope based on the post type.
324
         * This can be used by the replacevar plugin to determine if a replacement needs to be executed.
325
         *
326
         * @return string String describing the current scope.
327
         */
328
        private function determine_scope() {
×
329
                if ( $this->get_metabox_post()->post_type === 'page' ) {
×
330
                        return 'page';
×
331
                }
332

333
                return 'post';
×
334
        }
335

336
        /**
337
         * Outputs the meta box.
338
         *
339
         * @return void
340
         */
341
        public function meta_box() {
×
342
                $this->render_hidden_fields();
×
343
                $this->render_tabs();
×
344
        }
345

346
        /**
347
         * Renders the metabox hidden fields.
348
         *
349
         * @return void
350
         */
351
        protected function render_hidden_fields() {
×
352
                wp_nonce_field( 'yoast_free_metabox', 'yoast_free_metabox_nonce' );
×
353

354
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in class.
355
                echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'general' );
×
356

357
                if ( $this->is_advanced_metadata_enabled ) {
×
358
                        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in class.
359
                        echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'advanced' );
×
360
                }
361

362
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in class.
363
                echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'schema', $this->get_metabox_post()->post_type );
×
364

365
                if ( $this->social_is_enabled ) {
×
366
                        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in class.
367
                        echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'social' );
×
368
                }
369

370
                /**
371
                 * Filter: 'wpseo_content_meta_section_content' - Allow filtering the metabox content before outputting.
372
                 *
373
                 * @param string $post_content The metabox content string.
374
                 */
375
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output should be escaped in the filter.
376
                echo apply_filters( 'wpseo_content_meta_section_content', '' );
×
377
        }
378

379
        /**
380
         * Renders the metabox tabs.
381
         *
382
         * @return void
383
         */
384
        protected function render_tabs() {
×
385
                echo '<div class="wpseo-metabox-content">';
×
386
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: $this->get_product_title() returns a hard-coded string.
387
                printf( '<div class="wpseo-metabox-menu"><ul role="tablist" class="yoast-aria-tabs" aria-label="%s">', $this->get_product_title() );
×
388

389
                $tabs = $this->get_tabs();
×
390

391
                foreach ( $tabs as $tab ) {
×
392
                        if ( $tab->name === 'premium' ) {
×
393
                                continue;
×
394
                        }
395

396
                        $tab->display_link();
×
397
                }
398

399
                echo '</ul></div>';
×
400

401
                foreach ( $tabs as $tab ) {
×
402
                        $tab->display_content();
×
403
                }
404

405
                echo '</div>';
×
406
        }
407

408
        /**
409
         * Returns the relevant metabox tabs for the current view.
410
         *
411
         * @return WPSEO_Metabox_Section[]
412
         */
413
        private function get_tabs() {
×
414
                $tabs = [];
×
415

416
                $label = __( 'SEO', 'wordpress-seo' );
×
417
                if ( $this->seo_analysis->is_enabled() ) {
×
418
                        $label = '<span class="wpseo-score-icon-container" id="wpseo-seo-score-icon"></span>' . $label;
×
419
                }
420
                $tabs[] = new WPSEO_Metabox_Section_React( 'content', $label );
×
421

422
                if ( $this->readability_analysis->is_enabled() ) {
×
423
                        $tabs[] = new WPSEO_Metabox_Section_Readability();
×
424
                }
425

426
                if ( $this->inclusive_language_analysis->is_enabled() ) {
×
427
                        $tabs[] = new WPSEO_Metabox_Section_Inclusive_Language();
×
428
                }
429

430
                if ( $this->is_advanced_metadata_enabled ) {
×
431
                        $tabs[] = new WPSEO_Metabox_Section_React(
×
432
                                'schema',
×
433
                                '<span class="wpseo-schema-icon"></span>' . __( 'Schema', 'wordpress-seo' ),
×
434
                                ''
×
435
                        );
436
                }
437

438
                if ( $this->social_is_enabled ) {
×
439
                        $tabs[] = new WPSEO_Metabox_Section_React(
×
440
                                'social',
×
441
                                '<span class="dashicons dashicons-share"></span>' . __( 'Social', 'wordpress-seo' ),
×
442
                                '',
×
443
                                [
444
                                        'html_after' => '<div id="wpseo-section-social"></div>',
×
445
                                ]
446
                        );
447
                }
448

449
                $tabs = array_merge( $tabs, $this->get_additional_tabs() );
×
450

451
                return $tabs;
×
452
        }
453

454
        /**
455
         * Returns the metabox tabs that have been added by other plugins.
456
         *
457
         * @return WPSEO_Metabox_Section_Additional[]
458
         */
459
        protected function get_additional_tabs() {
8✔
460
                $tabs = [];
8✔
461

462
                /**
463
                 * Private filter: 'yoast_free_additional_metabox_sections'.
464
                 *
465
                 * Meant for internal use only. Allows adding additional tabs to the Yoast SEO metabox.
466
                 *
467
                 * @since 11.9
468
                 *
469
                 * @param array[] $tabs {
470
                 *     An array of arrays with tab specifications.
471
                 *
472
                 *     @type array $tab {
473
                 *          A tab specification.
474
                 *
475
                 *          @type string $name         The name of the tab. Used in the HTML IDs, href and aria properties.
476
                 *          @type string $link_content The content of the tab link.
477
                 *          @type string $content      The content of the tab.
478
                 *          @type array $options {
479
                 *              Optional. Extra options.
480
                 *
481
                 *              @type string $link_class      Optional. The class for the tab link.
482
                 *              @type string $link_aria_label Optional. The aria label of the tab link.
483
                 *          }
484
                 *     }
485
                 * }
486
                 */
487
                $requested_tabs = apply_filters( 'yoast_free_additional_metabox_sections', [] );
8✔
488

489
                foreach ( $requested_tabs as $tab ) {
8✔
490
                        if ( is_array( $tab ) && array_key_exists( 'name', $tab ) && array_key_exists( 'link_content', $tab ) && array_key_exists( 'content', $tab ) ) {
6✔
491
                                $options = array_key_exists( 'options', $tab ) ? $tab['options'] : [];
4✔
492
                                $tabs[]  = new WPSEO_Metabox_Section_Additional(
4✔
493
                                        $tab['name'],
4✔
494
                                        $tab['link_content'],
4✔
495
                                        $tab['content'],
4✔
496
                                        $options
4✔
497
                                );
2✔
498
                        }
499
                }
500

501
                return $tabs;
8✔
502
        }
503

504
        /**
505
         * Adds a line in the meta box.
506
         *
507
         * @todo [JRF] Check if $class is added appropriately everywhere.
508
         *
509
         * @param string[] $meta_field_def Contains the vars based on which output is generated.
510
         * @param string   $key            Internal key (without prefix).
511
         *
512
         * @return string
513
         */
514
        public function do_meta_box( $meta_field_def, $key = '' ) {
×
515
                $content      = '';
×
516
                $esc_form_key = esc_attr( WPSEO_Meta::$form_prefix . $key );
×
517
                $meta_value   = WPSEO_Meta::get_value( $key, $this->get_metabox_post()->ID );
×
518

519
                $class = '';
×
520
                if ( isset( $meta_field_def['class'] ) && $meta_field_def['class'] !== '' ) {
×
521
                        $class = ' ' . $meta_field_def['class'];
×
522
                }
523

524
                $placeholder = '';
×
525
                if ( isset( $meta_field_def['placeholder'] ) && $meta_field_def['placeholder'] !== '' ) {
×
526
                        $placeholder = $meta_field_def['placeholder'];
×
527
                }
528

529
                $aria_describedby = '';
×
530
                $description      = '';
×
531
                if ( isset( $meta_field_def['description'] ) ) {
×
532
                        $aria_describedby = ' aria-describedby="' . $esc_form_key . '-desc"';
×
533
                        $description      = '<p id="' . $esc_form_key . '-desc" class="yoast-metabox__description">' . $meta_field_def['description'] . '</p>';
×
534
                }
535

536
                // Add a hide_on_pages option that returns nothing when the field is rendered on a page.
537
                if ( isset( $meta_field_def['hide_on_pages'] ) && $meta_field_def['hide_on_pages'] && get_post_type() === 'page' ) {
×
538
                        return '';
×
539
                }
540

541
                switch ( $meta_field_def['type'] ) {
×
542
                        case 'text':
×
543
                                $ac = '';
×
544
                                if ( isset( $meta_field_def['autocomplete'] ) && $meta_field_def['autocomplete'] === false ) {
×
545
                                        $ac = 'autocomplete="off" ';
×
546
                                }
547
                                if ( $placeholder !== '' ) {
×
548
                                        $placeholder = ' placeholder="' . esc_attr( $placeholder ) . '"';
×
549
                                }
550
                                $content .= '<input type="text"' . $placeholder . ' id="' . $esc_form_key . '" ' . $ac . 'name="' . $esc_form_key . '" value="' . esc_attr( $meta_value ) . '" class="large-text' . $class . '"' . $aria_describedby . '/>';
×
551
                                break;
×
552

553
                        case 'url':
×
554
                                if ( $placeholder !== '' ) {
×
555
                                        $placeholder = ' placeholder="' . esc_attr( $placeholder ) . '"';
×
556
                                }
557
                                $content .= '<input type="url"' . $placeholder . ' id="' . $esc_form_key . '" name="' . $esc_form_key . '" value="' . esc_attr( urldecode( $meta_value ) ) . '" class="large-text' . $class . '"' . $aria_describedby . '/>';
×
558
                                break;
×
559

560
                        case 'textarea':
×
561
                                $rows = 3;
×
562
                                if ( isset( $meta_field_def['rows'] ) && $meta_field_def['rows'] > 0 ) {
×
563
                                        $rows = $meta_field_def['rows'];
×
564
                                }
565
                                $content .= '<textarea class="large-text' . $class . '" rows="' . esc_attr( $rows ) . '" id="' . $esc_form_key . '" name="' . $esc_form_key . '"' . $aria_describedby . '>' . esc_textarea( $meta_value ) . '</textarea>';
×
566
                                break;
×
567

568
                        case 'hidden':
×
569
                                $default = '';
×
570
                                if ( isset( $meta_field_def['default'] ) ) {
×
571
                                        $default = sprintf( ' data-default="%s"', esc_attr( $meta_field_def['default'] ) );
×
572
                                }
573
                                $content .= '<input type="hidden" id="' . $esc_form_key . '" name="' . $esc_form_key . '" value="' . esc_attr( $meta_value ) . '"' . $default . '/>' . "\n";
×
574
                                break;
×
575
                        case 'select':
×
576
                                if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== [] ) {
×
577
                                        $content .= '<select name="' . $esc_form_key . '" id="' . $esc_form_key . '" class="yoast' . $class . '">';
×
578
                                        foreach ( $meta_field_def['options'] as $val => $option ) {
×
579
                                                $selected = selected( $meta_value, $val, false );
×
580
                                                $content .= '<option ' . $selected . ' value="' . esc_attr( $val ) . '">' . esc_html( $option ) . '</option>';
×
581
                                        }
582
                                        unset( $val, $option, $selected );
×
583
                                        $content .= '</select>';
×
584
                                }
585
                                break;
×
586

587
                        case 'multiselect':
×
588
                                if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== [] ) {
×
589

590
                                        // Set $meta_value as $selected_arr.
591
                                        $selected_arr = $meta_value;
×
592

593
                                        // If the multiselect field is 'meta-robots-adv' we should explode on ,.
594
                                        if ( $key === 'meta-robots-adv' ) {
×
595
                                                $selected_arr = explode( ',', $meta_value );
×
596
                                        }
597

598
                                        if ( ! is_array( $selected_arr ) ) {
×
599
                                                $selected_arr = (array) $selected_arr;
×
600
                                        }
601

602
                                        $options_count = count( $meta_field_def['options'] );
×
603

604
                                        $content .= '<select multiple="multiple" size="' . esc_attr( $options_count ) . '" name="' . $esc_form_key . '[]" id="' . $esc_form_key . '" class="yoast' . $class . '"' . $aria_describedby . '>';
×
605
                                        foreach ( $meta_field_def['options'] as $val => $option ) {
×
606
                                                $selected = '';
×
607
                                                if ( in_array( $val, $selected_arr, true ) ) {
×
608
                                                        $selected = ' selected="selected"';
×
609
                                                }
610
                                                $content .= '<option ' . $selected . ' value="' . esc_attr( $val ) . '">' . esc_html( $option ) . '</option>';
×
611
                                        }
612
                                        $content .= '</select>';
×
613
                                        unset( $val, $option, $selected, $selected_arr, $options_count );
×
614
                                }
615
                                break;
×
616

617
                        case 'checkbox':
×
618
                                $checked  = checked( $meta_value, 'on', false );
×
619
                                $expl     = ( isset( $meta_field_def['expl'] ) ) ? esc_html( $meta_field_def['expl'] ) : '';
×
620
                                $content .= '<input type="checkbox" id="' . $esc_form_key . '" name="' . $esc_form_key . '" ' . $checked . ' value="on" class="yoast' . $class . '"' . $aria_describedby . '/> <label for="' . $esc_form_key . '">' . $expl . '</label>';
×
621
                                unset( $checked, $expl );
×
622
                                break;
×
623

624
                        case 'radio':
×
625
                                if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== [] ) {
×
626
                                        foreach ( $meta_field_def['options'] as $val => $option ) {
×
627
                                                $checked  = checked( $meta_value, $val, false );
×
628
                                                $content .= '<input type="radio" ' . $checked . ' id="' . $esc_form_key . '_' . esc_attr( $val ) . '" name="' . $esc_form_key . '" value="' . esc_attr( $val ) . '"/> <label for="' . $esc_form_key . '_' . esc_attr( $val ) . '">' . esc_html( $option ) . '</label> ';
×
629
                                        }
630
                                        unset( $val, $option, $checked );
×
631
                                }
632
                                break;
×
633

634
                        case 'upload':
×
635
                                $content .= '<input'
636
                                        . ' id="' . $esc_form_key . '"'
×
637
                                        . ' type="text"'
×
638
                                        . ' size="36"'
×
639
                                        . ' class="' . $class . '"'
×
640
                                        . ' name="' . $esc_form_key . '"'
×
641
                                        . ' value="' . esc_attr( $meta_value ) . '"' . $aria_describedby
×
642
                                        . ' readonly="readonly"'
×
643
                                        . ' /> ';
×
644
                                $content .= '<input'
645
                                        . ' id="' . esc_attr( $esc_form_key ) . '_button"'
×
646
                                        . ' class="wpseo_image_upload_button button"'
×
647
                                        . ' data-target="' . esc_attr( $esc_form_key ) . '"'
×
648
                                        . ' data-target-id="' . esc_attr( $esc_form_key ) . '-id"'
×
649
                                        . ' type="button"'
×
650
                                        . ' value="' . esc_attr__( 'Upload Image', 'wordpress-seo' ) . '"'
×
651
                                        . ' /> ';
×
652
                                $content .= '<input'
653
                                        . ' class="wpseo_image_remove_button button"'
654
                                        . ' type="button"'
655
                                        . ' value="' . esc_attr__( 'Clear Image', 'wordpress-seo' ) . '"'
×
656
                                        . ' />';
×
657
                                break;
×
658
                }
659

660
                $html = '';
×
661
                if ( $content === '' ) {
×
662
                        $content = apply_filters( 'wpseo_do_meta_box_field_' . $key, $content, $meta_value, $esc_form_key, $meta_field_def, $key );
×
663
                }
664

665
                if ( $content !== '' ) {
×
666

667
                        $title = esc_html( $meta_field_def['title'] );
×
668

669
                        // By default, use the field title as a label element.
670
                        $label = '<label for="' . $esc_form_key . '">' . $title . '</label>';
×
671

672
                        // Set the inline help and help panel, if any.
673
                        $help_button = '';
×
674
                        $help_panel  = '';
×
675
                        if ( isset( $meta_field_def['help'] ) && $meta_field_def['help'] !== '' ) {
×
676
                                $help        = new WPSEO_Admin_Help_Panel( $key, $meta_field_def['help-button'], $meta_field_def['help'] );
×
677
                                $help_button = $help->get_button_html();
×
678
                                $help_panel  = $help->get_panel_html();
×
679
                        }
680

681
                        // If it's a set of radio buttons, output proper fieldset and legend.
682
                        if ( $meta_field_def['type'] === 'radio' ) {
×
683
                                return '<fieldset><legend>' . $title . '</legend>' . $help_button . $help_panel . $content . $description . '</fieldset>';
×
684
                        }
685

686
                        // If it's a single checkbox, ignore the title.
687
                        if ( $meta_field_def['type'] === 'checkbox' ) {
×
688
                                $label = '';
×
689
                        }
690

691
                        // Other meta box content or form fields.
692
                        if ( $meta_field_def['type'] === 'hidden' ) {
×
693
                                $html = $content;
×
694
                        }
695
                        else {
696
                                $html = $label . $description . $help_button . $help_panel . $content;
×
697
                        }
698
                }
699

700
                return $html;
×
701
        }
702

703
        /**
704
         * Saves the WP SEO metadata for posts.
705
         *
706
         * {@internal $_POST parameters are validated via sanitize_post_meta().}}
707
         *
708
         * @param int $post_id Post ID.
709
         *
710
         * @return bool|void Boolean false if invalid save post request.
711
         */
UNCOV
712
        public function save_postdata( $post_id ) {
×
713
                // Bail if this is a multisite installation and the site has been switched.
UNCOV
714
                if ( is_multisite() && ms_is_switched() ) {
×
715
                        return false;
×
716
                }
717

UNCOV
718
                if ( $post_id === null ) {
×
719
                        return false;
×
720
                }
721

722
                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized in wp_verify_none.
UNCOV
723
                if ( ! isset( $_POST['yoast_free_metabox_nonce'] ) || ! wp_verify_nonce( wp_unslash( $_POST['yoast_free_metabox_nonce'] ), 'yoast_free_metabox' ) ) {
×
724
                        return false;
×
725
                }
726

UNCOV
727
                if ( wp_is_post_revision( $post_id ) ) {
×
728
                        $post_id = wp_is_post_revision( $post_id );
×
729
                }
730

731
                /**
732
                 * Determine we're not accidentally updating a different post.
733
                 * We can't use filter_input here as the ID isn't available at this point, other than in the $_POST data.
734
                 */
UNCOV
735
                if ( ! isset( $_POST['ID'] ) || $post_id !== (int) $_POST['ID'] ) {
×
736
                        return false;
×
737
                }
738

UNCOV
739
                clean_post_cache( $post_id );
×
UNCOV
740
                $post = get_post( $post_id );
×
741

UNCOV
742
                if ( ! is_object( $post ) ) {
×
743
                        // Non-existent post.
744
                        return false;
×
745
                }
746

UNCOV
747
                do_action( 'wpseo_save_compare_data', $post );
×
748

UNCOV
749
                $social_fields = [];
×
UNCOV
750
                if ( $this->social_is_enabled ) {
×
UNCOV
751
                        $social_fields = WPSEO_Meta::get_meta_field_defs( 'social' );
×
752
                }
753

UNCOV
754
                $meta_boxes = apply_filters( 'wpseo_save_metaboxes', [] );
×
UNCOV
755
                $meta_boxes = array_merge(
×
UNCOV
756
                        $meta_boxes,
×
UNCOV
757
                        WPSEO_Meta::get_meta_field_defs( 'general', $post->post_type ),
×
UNCOV
758
                        WPSEO_Meta::get_meta_field_defs( 'advanced' ),
×
UNCOV
759
                        $social_fields,
×
UNCOV
760
                        WPSEO_Meta::get_meta_field_defs( 'schema', $post->post_type )
×
761
                );
762

UNCOV
763
                foreach ( $meta_boxes as $key => $meta_box ) {
×
764

765
                        // If analysis is disabled remove that analysis score value from the DB.
UNCOV
766
                        if ( $this->is_meta_value_disabled( $key ) ) {
×
UNCOV
767
                                WPSEO_Meta::delete( $key, $post_id );
×
UNCOV
768
                                continue;
×
769
                        }
770

UNCOV
771
                        $data       = null;
×
UNCOV
772
                        $field_name = WPSEO_Meta::$form_prefix . $key;
×
773

UNCOV
774
                        if ( $meta_box['type'] === 'checkbox' ) {
×
775
                                $data = isset( $_POST[ $field_name ] ) ? 'on' : 'off';
×
776
                        }
777
                        else {
UNCOV
778
                                if ( isset( $_POST[ $field_name ] ) ) {
×
779
                                        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We're preparing to do just that.
UNCOV
780
                                        $data = wp_unslash( $_POST[ $field_name ] );
×
781

782
                                        // For multi-select.
UNCOV
783
                                        if ( is_array( $data ) ) {
×
UNCOV
784
                                                $data = array_map( [ 'WPSEO_Utils', 'sanitize_text_field' ], $data );
×
785
                                        }
786

UNCOV
787
                                        if ( is_string( $data ) ) {
×
UNCOV
788
                                                $data = ( $key !== 'canonical' ) ? WPSEO_Utils::sanitize_text_field( $data ) : WPSEO_Utils::sanitize_url( $data );
×
789
                                        }
790
                                }
791

792
                                // Reset options when no entry is present with multiselect - only applies to `meta-robots-adv` currently.
UNCOV
793
                                if ( ! isset( $_POST[ $field_name ] ) && ( $meta_box['type'] === 'multiselect' ) ) {
×
794
                                        $data = [];
×
795
                                }
796
                        }
797

UNCOV
798
                        if ( $data !== null ) {
×
UNCOV
799
                                WPSEO_Meta::set_value( $key, $data, $post_id );
×
800
                        }
801
                }
802

UNCOV
803
                do_action( 'wpseo_saved_postdata' );
×
804
        }
805

806
        /**
807
         * Determines if the given meta value key is disabled.
808
         *
809
         * @param string $key The key of the meta value.
810
         *
811
         * @return bool Whether the given meta value key is disabled.
812
         */
813
        public function is_meta_value_disabled( $key ) {
×
814
                if ( $key === 'linkdex' && ! $this->seo_analysis->is_enabled() ) {
×
815
                        return true;
×
816
                }
817

818
                if ( $key === 'content_score' && ! $this->readability_analysis->is_enabled() ) {
×
819
                        return true;
×
820
                }
821

822
                if ( $key === 'inclusive_language_score' && ! $this->inclusive_language_analysis->is_enabled() ) {
×
823
                        return true;
×
824
                }
825

826
                return false;
×
827
        }
828

829
        /**
830
         * Enqueues all the needed JS and CSS.
831
         *
832
         * @todo [JRF => whomever] Create css/metabox-mp6.css file and add it to the below allowed colors array when done.
833
         *
834
         * @return void
835
         */
UNCOV
836
        public function enqueue() {
×
UNCOV
837
                global $pagenow;
×
838

UNCOV
839
                $asset_manager = new WPSEO_Admin_Asset_Manager();
×
840

UNCOV
841
                $is_editor = self::is_post_overview( $pagenow ) || self::is_post_edit( $pagenow );
×
842

UNCOV
843
                if ( self::is_post_overview( $pagenow ) ) {
×
844
                        $asset_manager->enqueue_style( 'edit-page' );
×
845
                        $asset_manager->enqueue_script( 'edit-page' );
×
846

847
                        return;
×
848
                }
849

850
                /* Filter 'wpseo_always_register_metaboxes_on_admin' documented in wpseo-main.php */
UNCOV
851
                if ( ( $is_editor === false && apply_filters( 'wpseo_always_register_metaboxes_on_admin', false ) === false ) || $this->display_metabox() === false ) {
×
UNCOV
852
                        return;
×
853
                }
854

855
                $post_id = get_queried_object_id();
×
856
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
857
                if ( empty( $post_id ) && isset( $_GET['post'] ) && is_string( $_GET['post'] ) ) {
×
858
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
859
                        $post_id = sanitize_text_field( wp_unslash( $_GET['post'] ) );
×
860
                }
861

862
                if ( $post_id !== 0 ) {
×
863
                        // Enqueue files needed for upload functionality.
864
                        wp_enqueue_media( [ 'post' => $post_id ] );
×
865
                }
866

867
                $asset_manager->enqueue_style( 'metabox-css' );
×
868
                $asset_manager->enqueue_style( 'scoring' );
×
869
                $asset_manager->enqueue_style( 'monorepo' );
×
870
                $asset_manager->enqueue_style( 'ai-generator' );
×
871

872
                $is_block_editor  = WP_Screen::get()->is_block_editor();
×
873
                $post_edit_handle = 'post-edit';
×
874
                if ( ! $is_block_editor ) {
×
875
                        $post_edit_handle = 'post-edit-classic';
×
876
                }
877
                $asset_manager->enqueue_script( $post_edit_handle );
×
878
                $asset_manager->enqueue_style( 'admin-css' );
×
879

880
                /**
881
                 * Removes the emoji script as it is incompatible with both React and any
882
                 * contenteditable fields.
883
                 */
884
                remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
×
885

886
                $asset_manager->localize_script( $post_edit_handle, 'wpseoAdminL10n', WPSEO_Utils::get_admin_l10n() );
×
887

888
                $plugins_script_data = [
889
                        'replaceVars' => [
890
                                'no_parent_text'           => __( '(no parent)', 'wordpress-seo' ),
×
891
                                'replace_vars'             => $this->get_replace_vars(),
×
892
                                'hidden_replace_vars'      => $this->get_hidden_replace_vars(),
×
893
                                'recommended_replace_vars' => $this->get_recommended_replace_vars(),
×
894
                                'scope'                    => $this->determine_scope(),
×
895
                                'has_taxonomies'           => $this->current_post_type_has_taxonomies(),
×
896
                        ],
897
                        'shortcodes' => [
898
                                'wpseo_shortcode_tags'          => $this->get_valid_shortcode_tags(),
×
899
                                'wpseo_filter_shortcodes_nonce' => wp_create_nonce( 'wpseo-filter-shortcodes' ),
×
900
                        ],
901
                ];
902

903
                $worker_script_data = [
904
                        'url'                     => YoastSEO()->helpers->asset->get_asset_url( 'yoast-seo-analysis-worker' ),
×
905
                        'dependencies'            => YoastSEO()->helpers->asset->get_dependency_urls_by_handle( 'yoast-seo-analysis-worker' ),
×
906
                        'keywords_assessment_url' => YoastSEO()->helpers->asset->get_asset_url( 'yoast-seo-used-keywords-assessment' ),
×
907
                        'log_level'               => WPSEO_Utils::get_analysis_worker_log_level(),
×
908
                ];
909

910
                $alert_dismissal_action            = YoastSEO()->classes->get( Alert_Dismissal_Action::class );
×
911
                $dismissed_alerts                  = $alert_dismissal_action->all_dismissed();
×
912
                $woocommerce_conditional           = new WooCommerce_Conditional();
×
913
                $woocommerce_active                = $woocommerce_conditional->is_met();
×
914
                $wpseo_plugin_availability_checker = new WPSEO_Plugin_Availability();
×
915
                $woocommerce_seo_file              = 'wpseo-woocommerce/wpseo-woocommerce.php';
×
916
                $woocommerce_seo_active            = $wpseo_plugin_availability_checker->is_active( $woocommerce_seo_file );
×
917

918
                $script_data = [
919
                        // @todo replace this translation with JavaScript translations.
920
                        'media'                      => [ 'choose_image' => __( 'Use Image', 'wordpress-seo' ) ],
×
921
                        'metabox'                    => $this->get_metabox_script_data(),
×
922
                        'userLanguageCode'           => WPSEO_Language_Utils::get_language( get_user_locale() ),
×
923
                        'isPost'                     => true,
924
                        'isBlockEditor'              => $is_block_editor,
×
925
                        'postId'                     => $post_id,
×
926
                        'postStatus'                 => get_post_status( $post_id ),
×
927
                        'postType'                   => get_post_type( $post_id ),
×
928
                        'usedKeywordsNonce'          => wp_create_nonce( 'wpseo-keyword-usage-and-post-types' ),
×
929
                        'analysis'                   => [
930
                                'plugins' => $plugins_script_data,
×
931
                                'worker'  => $worker_script_data,
×
932
                        ],
933
                        'dismissedAlerts'            => $dismissed_alerts,
×
934
                        'currentPromotions'          => YoastSEO()->classes->get( Promotion_Manager::class )->get_current_promotions(),
×
935
                        'webinarIntroBlockEditorUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/webinar-intro-block-editor' ),
×
936
                        'blackFridayBlockEditorUrl'  => ( YoastSEO()->classes->get( Promotion_Manager::class )->is( 'black-friday-2023-checklist' ) ) ? WPSEO_Shortlinker::get( 'https://yoa.st/black-friday-checklist' ) : '',
×
937
                        'isJetpackBoostActive'       => ( $is_block_editor ) ? YoastSEO()->classes->get( Jetpack_Boost_Active_Conditional::class )->is_met() : false,
×
938
                        'isJetpackBoostNotPremium'   => ( $is_block_editor ) ? YoastSEO()->classes->get( Jetpack_Boost_Not_Premium_Conditional::class )->is_met() : false,
×
939
                        'isWooCommerceSeoActive'     => $woocommerce_seo_active,
×
940
                        'isWooCommerceActive'        => $woocommerce_active,
×
941
                        'woocommerceUpsell'          => get_post_type( $post_id ) === 'product' && ! $woocommerce_seo_active && $woocommerce_active,
×
942
                        'linkParams'                 => WPSEO_Shortlinker::get_query_params(),
×
943
                        'pluginUrl'                  => plugins_url( '', WPSEO_FILE ),
×
944
                        'wistiaEmbedPermission'      => YoastSEO()->classes->get( Wistia_Embed_Permission_Repository::class )->get_value_for_user( get_current_user_id() ),
×
945
                ];
946

947
                if ( post_type_supports( get_post_type(), 'thumbnail' ) ) {
×
948
                        $asset_manager->enqueue_style( 'featured-image' );
×
949

950
                        // @todo replace this translation with JavaScript translations.
951
                        $script_data['featuredImage'] = [
×
952
                                'featured_image_notice' => __( 'SEO issue: The featured image should be at least 200 by 200 pixels to be picked up by Facebook and other social media sites.', 'wordpress-seo' ),
×
953
                        ];
954
                }
955

956
                $asset_manager->localize_script( $post_edit_handle, 'wpseoScriptData', $script_data );
×
957
                $asset_manager->enqueue_user_language_script();
×
958
        }
959

960
        /**
961
         * Returns post in metabox context.
962
         *
963
         * @return WP_Post|array<string|int|bool>
964
         */
965
        protected function get_metabox_post() {
×
966
                if ( $this->post !== null ) {
×
967
                        return $this->post;
×
968
                }
969

970
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
971
                if ( isset( $_GET['post'] ) && is_string( $_GET['post'] ) ) {
×
972
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information, Sanitization happens in the validate_int function.
973
                        $post_id = (int) WPSEO_Utils::validate_int( wp_unslash( $_GET['post'] ) );
×
974

975
                        $this->post = get_post( $post_id );
×
976

977
                        return $this->post;
×
978
                }
979

980
                if ( isset( $GLOBALS['post'] ) ) {
×
981
                        $this->post = $GLOBALS['post'];
×
982

983
                        return $this->post;
×
984
                }
985

986
                return [];
×
987
        }
988

989
        /**
990
         * Returns an array with shortcode tags for all registered shortcodes.
991
         *
992
         * @return string[]
993
         */
994
        private function get_valid_shortcode_tags() {
×
995
                $shortcode_tags = [];
×
996

997
                foreach ( $GLOBALS['shortcode_tags'] as $tag => $description ) {
×
998
                        $shortcode_tags[] = $tag;
×
999
                }
1000

1001
                return $shortcode_tags;
×
1002
        }
1003

1004
        /**
1005
         * Prepares the replace vars for localization.
1006
         *
1007
         * @return string[] Replace vars.
1008
         */
1009
        private function get_replace_vars() {
×
1010
                $cached_replacement_vars = [];
×
1011

1012
                $vars_to_cache = [
1013
                        'date',
×
1014
                        'id',
1015
                        'sitename',
1016
                        'sitedesc',
1017
                        'sep',
1018
                        'page',
1019
                        'currentdate',
1020
                        'currentyear',
1021
                        'currentmonth',
1022
                        'currentday',
1023
                        'post_year',
1024
                        'post_month',
1025
                        'post_day',
1026
                        'name',
1027
                        'author_first_name',
1028
                        'author_last_name',
1029
                        'permalink',
1030
                        'post_content',
1031
                        'category_title',
1032
                        'tag',
1033
                        'category',
1034
                ];
1035

1036
                foreach ( $vars_to_cache as $var ) {
×
1037
                        $cached_replacement_vars[ $var ] = wpseo_replace_vars( '%%' . $var . '%%', $this->get_metabox_post() );
×
1038
                }
1039

1040
                // Merge custom replace variables with the WordPress ones.
1041
                return array_merge( $cached_replacement_vars, $this->get_custom_replace_vars( $this->get_metabox_post() ) );
×
1042
        }
1043

1044
        /**
1045
         * Returns the list of replace vars that should be hidden inside the editor.
1046
         *
1047
         * @return string[] The hidden replace vars.
1048
         */
1049
        protected function get_hidden_replace_vars() {
×
1050
                return ( new WPSEO_Replace_Vars() )->get_hidden_replace_vars();
×
1051
        }
1052

1053
        /**
1054
         * Prepares the recommended replace vars for localization.
1055
         *
1056
         * @return array<string[]> Recommended replacement variables.
1057
         */
1058
        private function get_recommended_replace_vars() {
×
1059
                $recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
×
1060

1061
                // What is recommended depends on the current context.
1062
                $post_type = $recommended_replace_vars->determine_for_post( $this->get_metabox_post() );
×
1063

1064
                return $recommended_replace_vars->get_recommended_replacevars_for( $post_type );
×
1065
        }
1066

1067
        /**
1068
         * Gets the custom replace variables for custom taxonomies and fields.
1069
         *
1070
         * @param WP_Post $post The post to check for custom taxonomies and fields.
1071
         *
1072
         * @return array<string[]> Array containing all the replacement variables.
1073
         */
1074
        private function get_custom_replace_vars( $post ) {
×
1075
                return [
1076
                        'custom_fields'     => $this->get_custom_fields_replace_vars( $post ),
×
1077
                        'custom_taxonomies' => $this->get_custom_taxonomies_replace_vars( $post ),
×
1078
                ];
1079
        }
1080

1081
        /**
1082
         * Gets the custom replace variables for custom taxonomies.
1083
         *
1084
         * @param WP_Post $post The post to check for custom taxonomies.
1085
         *
1086
         * @return array<string[]> Array containing all the replacement variables.
1087
         */
1088
        private function get_custom_taxonomies_replace_vars( $post ) {
×
1089
                $taxonomies          = get_object_taxonomies( $post, 'objects' );
×
1090
                $custom_replace_vars = [];
×
1091

1092
                foreach ( $taxonomies as $taxonomy_name => $taxonomy ) {
×
1093

1094
                        if ( is_string( $taxonomy ) ) { // If attachment, see https://core.trac.wordpress.org/ticket/37368 .
×
1095
                                $taxonomy_name = $taxonomy;
×
1096
                                $taxonomy      = get_taxonomy( $taxonomy_name );
×
1097
                        }
1098

1099
                        if ( $taxonomy->_builtin && $taxonomy->public ) {
×
1100
                                continue;
×
1101
                        }
1102

1103
                        $custom_replace_vars[ $taxonomy_name ] = [
×
1104
                                'name'        => $taxonomy->name,
×
1105
                                'description' => $taxonomy->description,
×
1106
                        ];
1107
                }
1108

1109
                return $custom_replace_vars;
×
1110
        }
1111

1112
        /**
1113
         * Gets the custom replace variables for custom fields.
1114
         *
1115
         * @param WP_Post $post The post to check for custom fields.
1116
         *
1117
         * @return array<string[]> Array containing all the replacement variables.
1118
         */
1119
        private function get_custom_fields_replace_vars( $post ) {
×
1120
                $custom_replace_vars = [];
×
1121

1122
                // If no post object is passed, return the empty custom_replace_vars array.
1123
                if ( ! is_object( $post ) ) {
×
1124
                        return $custom_replace_vars;
×
1125
                }
1126

1127
                $custom_fields = get_post_custom( $post->ID );
×
1128

1129
                // If $custom_fields is an empty string or generally not an array, return early.
1130
                if ( ! is_array( $custom_fields ) ) {
×
1131
                        return $custom_replace_vars;
×
1132
                }
1133

1134
                $meta = YoastSEO()->meta->for_post( $post->ID );
×
1135

1136
                if ( ! $meta ) {
×
1137
                        return $custom_replace_vars;
×
1138
                }
1139

1140
                // Simply concatenate all fields containing replace vars so we can handle them all with a single regex find.
1141
                $replace_vars_fields = implode(
×
1142
                        ' ',
×
1143
                        [
1144
                                $meta->presentation->title,
×
1145
                                $meta->presentation->meta_description,
×
1146
                        ]
1147
                );
1148

1149
                preg_match_all( '/%%cf_([A-Za-z0-9_]+)%%/', $replace_vars_fields, $matches );
×
1150
                $fields_to_include = $matches[1];
×
1151
                foreach ( $custom_fields as $custom_field_name => $custom_field ) {
×
1152
                        // Skip private custom fields.
1153
                        if ( substr( $custom_field_name, 0, 1 ) === '_' ) {
×
1154
                                continue;
×
1155
                        }
1156

1157
                        // Skip custom fields that are not used, new ones will be fetched dynamically.
1158
                        if ( ! in_array( $custom_field_name, $fields_to_include, true ) ) {
×
1159
                                continue;
×
1160
                        }
1161

1162
                        // Skip custom field values that are serialized.
1163
                        if ( is_serialized( $custom_field[0] ) ) {
×
1164
                                continue;
×
1165
                        }
1166

1167
                        $custom_replace_vars[ $custom_field_name ] = $custom_field[0];
×
1168
                }
1169

1170
                return $custom_replace_vars;
×
1171
        }
1172

1173
        /**
1174
         * Checks if the page is the post overview page.
1175
         *
1176
         * @param string $page The page to check for the post overview page.
1177
         *
1178
         * @return bool Whether or not the given page is the post overview page.
1179
         */
1180
        public static function is_post_overview( $page ) {
×
1181
                return $page === 'edit.php';
×
1182
        }
1183

1184
        /**
1185
         * Checks if the page is the post edit page.
1186
         *
1187
         * @param string $page The page to check for the post edit page.
1188
         *
1189
         * @return bool Whether or not the given page is the post edit page.
1190
         */
1191
        public static function is_post_edit( $page ) {
×
1192
                return $page === 'post.php'
×
1193
                        || $page === 'post-new.php';
×
1194
        }
1195

1196
        /**
1197
         * Retrieves the product title.
1198
         *
1199
         * @return string The product title.
1200
         */
1201
        protected function get_product_title() {
×
1202
                return YoastSEO()->helpers->product->get_product_name();
×
1203
        }
1204
}
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