• 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

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

8
use Yoast\WP\SEO\Introductions\Infrastructure\Wistia_Embed_Permission_Repository;
9
use Yoast\WP\SEO\Presenters\Admin\Alert_Presenter;
10

11
/**
12
 * Class that handles the edit boxes on taxonomy edit pages.
13
 */
14
class WPSEO_Taxonomy {
15

16
        /**
17
         * The current active taxonomy.
18
         *
19
         * @var string
20
         */
21
        private $taxonomy = '';
22

23
        /**
24
         * Holds the metabox SEO analysis instance.
25
         *
26
         * @var WPSEO_Metabox_Analysis_SEO
27
         */
28
        private $analysis_seo;
29

30
        /**
31
         * Holds the metabox readability analysis instance.
32
         *
33
         * @var WPSEO_Metabox_Analysis_Readability
34
         */
35
        private $analysis_readability;
36

37
        /**
38
         * Holds the metabox inclusive language analysis instance.
39
         *
40
         * @var WPSEO_Metabox_Analysis_Inclusive_Language
41
         */
42
        private $analysis_inclusive_language;
43

44
        /**
45
         * Class constructor.
46
         */
47
        public function __construct() {
×
48
                $this->taxonomy = $this::get_taxonomy();
×
49

50
                add_action( 'edit_term', [ $this, 'update_term' ], 99, 3 );
×
51
                add_action( 'init', [ $this, 'custom_category_descriptions_allow_html' ] );
×
52
                add_action( 'admin_init', [ $this, 'admin_init' ] );
×
53

54
                if ( self::is_term_overview( $GLOBALS['pagenow'] ) ) {
×
55
                        new WPSEO_Taxonomy_Columns();
×
56
                }
57
                $this->analysis_seo                = new WPSEO_Metabox_Analysis_SEO();
×
58
                $this->analysis_readability        = new WPSEO_Metabox_Analysis_Readability();
×
59
                $this->analysis_inclusive_language = new WPSEO_Metabox_Analysis_Inclusive_Language();
×
60
        }
61

62
        /**
63
         * Add hooks late enough for taxonomy object to be available for checks.
64
         *
65
         * @return void
66
         */
67
        public function admin_init() {
×
68

69
                $taxonomy = get_taxonomy( $this->taxonomy );
×
70

71
                if ( empty( $taxonomy ) || empty( $taxonomy->public ) || ! $this->show_metabox() ) {
×
72
                        return;
×
73
                }
74

75
                // Adds custom category description editor. Needs a hook that runs before the description field.
76
                add_action( "{$this->taxonomy}_term_edit_form_top", [ $this, 'custom_category_description_editor' ] );
×
77

78
                add_action( sanitize_text_field( $this->taxonomy ) . '_edit_form', [ $this, 'term_metabox' ], 90, 1 );
×
79
                add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
×
80
        }
81

82
        /**
83
         * Show the SEO inputs for term.
84
         *
85
         * @param stdClass|WP_Term $term Term to show the edit boxes for.
86
         *
87
         * @return void
88
         */
89
        public function term_metabox( $term ) {
×
90
                if ( WPSEO_Metabox::is_internet_explorer() ) {
×
91
                        $this->show_internet_explorer_notice();
×
92
                        return;
×
93
                }
94

95
                $metabox = new WPSEO_Taxonomy_Metabox( $this->taxonomy, $term );
×
96
                $metabox->display();
×
97
        }
98

99
        /**
100
         * Renders the content for the internet explorer metabox.
101
         *
102
         * @return void
103
         */
104
        private function show_internet_explorer_notice() {
×
105
                $product_title = YoastSEO()->helpers->product->get_product_name();
×
106

107
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: $product_title is hardcoded.
108
                printf( '<div id="wpseo_meta" class="postbox yoast wpseo-taxonomy-metabox-postbox"><h2><span>%1$s</span></h2>', $product_title );
×
109
                echo '<div class="inside">';
×
110

111
                $content = sprintf(
×
112
                        /* 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. */
113
                        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' ),
×
114
                        '<a href="https://www.mozilla.org/firefox/new/">',
×
115
                        '<a href="https://www.google.com/chrome/">',
×
116
                        '<a href="https://www.microsoft.com/windows/microsoft-edge">',
×
117
                        '</a>'
×
UNCOV
118
                );
×
119
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped above.
120
                echo new Alert_Presenter( $content );
×
121

122
                echo '</div></div>';
×
123
        }
124

125
        /**
126
         * Queue assets for taxonomy screens.
127
         *
128
         * @since 1.5.0
129
         *
130
         * @return void
131
         */
UNCOV
132
        public function admin_enqueue_scripts() {
×
133

UNCOV
134
                $pagenow = $GLOBALS['pagenow'];
×
135

UNCOV
136
                if ( ! ( self::is_term_edit( $pagenow ) || self::is_term_overview( $pagenow ) ) ) {
×
UNCOV
137
                        return;
×
138
                }
139

UNCOV
140
                $asset_manager = new WPSEO_Admin_Asset_Manager();
×
UNCOV
141
                $asset_manager->enqueue_style( 'scoring' );
×
UNCOV
142
                $asset_manager->enqueue_style( 'monorepo' );
×
143

UNCOV
144
                $tag_id = $this::get_tag_id();
×
145

146
                if (
UNCOV
147
                        self::is_term_edit( $pagenow )
×
UNCOV
148
                        && ! is_null( $tag_id )
×
149
                ) {
UNCOV
150
                        wp_enqueue_media(); // Enqueue files needed for upload functionality.
×
151

UNCOV
152
                        $asset_manager->enqueue_style( 'metabox-css' );
×
UNCOV
153
                        $asset_manager->enqueue_style( 'ai-generator' );
×
UNCOV
154
                        $asset_manager->enqueue_script( 'term-edit' );
×
155

156
                        /**
157
                         * Remove the emoji script as it is incompatible with both React and any
158
                         * contenteditable fields.
159
                         */
UNCOV
160
                        remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
×
161

UNCOV
162
                        $asset_manager->localize_script( 'term-edit', 'wpseoAdminL10n', WPSEO_Utils::get_admin_l10n() );
×
163

UNCOV
164
                        $script_data = [
×
UNCOV
165
                                'analysis'              => [
×
UNCOV
166
                                        'plugins' => [
×
UNCOV
167
                                                'replaceVars' => [
×
UNCOV
168
                                                        'no_parent_text'           => __( '(no parent)', 'wordpress-seo' ),
×
UNCOV
169
                                                        'replace_vars'             => $this->get_replace_vars(),
×
UNCOV
170
                                                        'recommended_replace_vars' => $this->get_recommended_replace_vars(),
×
UNCOV
171
                                                        'scope'                    => $this->determine_scope(),
×
UNCOV
172
                                                ],
×
UNCOV
173
                                                'shortcodes' => [
×
UNCOV
174
                                                        'wpseo_shortcode_tags'          => $this->get_valid_shortcode_tags(),
×
UNCOV
175
                                                        'wpseo_filter_shortcodes_nonce' => wp_create_nonce( 'wpseo-filter-shortcodes' ),
×
UNCOV
176
                                                ],
×
UNCOV
177
                                        ],
×
UNCOV
178
                                        'worker'  => [
×
UNCOV
179
                                                'url'                     => YoastSEO()->helpers->asset->get_asset_url( 'yoast-seo-analysis-worker' ),
×
UNCOV
180
                                                'dependencies'            => YoastSEO()->helpers->asset->get_dependency_urls_by_handle( 'yoast-seo-analysis-worker' ),
×
UNCOV
181
                                                'keywords_assessment_url' => YoastSEO()->helpers->asset->get_asset_url( 'yoast-seo-used-keywords-assessment' ),
×
UNCOV
182
                                                'log_level'               => WPSEO_Utils::get_analysis_worker_log_level(),
×
UNCOV
183
                                        ],
×
UNCOV
184
                                ],
×
UNCOV
185
                                'media'                 => [
×
186
                                        // @todo replace this translation with JavaScript translations.
UNCOV
187
                                        'choose_image' => __( 'Use Image', 'wordpress-seo' ),
×
UNCOV
188
                                ],
×
UNCOV
189
                                'metabox'               => $this->localize_term_scraper_script( $tag_id ),
×
UNCOV
190
                                'userLanguageCode'      => WPSEO_Language_Utils::get_language( get_user_locale() ),
×
UNCOV
191
                                'isTerm'                => true,
×
UNCOV
192
                                'postId'                => $tag_id,
×
UNCOV
193
                                'termType'              => $this->get_taxonomy(),
×
UNCOV
194
                                'usedKeywordsNonce'     => wp_create_nonce( 'wpseo-keyword-usage' ),
×
UNCOV
195
                                'linkParams'            => WPSEO_Shortlinker::get_query_params(),
×
UNCOV
196
                                'pluginUrl'             => plugins_url( '', WPSEO_FILE ),
×
UNCOV
197
                                'wistiaEmbedPermission' => YoastSEO()->classes->get( Wistia_Embed_Permission_Repository::class )->get_value_for_user( get_current_user_id() ),
×
UNCOV
198
                        ];
×
UNCOV
199
                        $asset_manager->localize_script( 'term-edit', 'wpseoScriptData', $script_data );
×
UNCOV
200
                        $asset_manager->enqueue_user_language_script();
×
201
                }
202

UNCOV
203
                if ( self::is_term_overview( $pagenow ) ) {
×
UNCOV
204
                        $asset_manager->enqueue_script( 'edit-page' );
×
205
                }
206
        }
207

208
        /**
209
         * Update the taxonomy meta data on save.
210
         *
211
         * @param int    $term_id  ID of the term to save data for.
212
         * @param int    $tt_id    The taxonomy_term_id for the term.
213
         * @param string $taxonomy The taxonomy the term belongs to.
214
         *
215
         * @return void
216
         */
217
        public function update_term( $term_id, $tt_id, $taxonomy ) {
×
218
                // Bail if this is a multisite installation and the site has been switched.
219
                if ( is_multisite() && ms_is_switched() ) {
×
220
                        return;
×
221
                }
222

223
                /* Create post array with only our values. */
224
                $new_meta_data = [];
×
225
                foreach ( WPSEO_Taxonomy_Meta::$defaults_per_term as $key => $default ) {
×
226
                        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: Nonce is already checked by WordPress before executing this action.
227
                        if ( isset( $_POST[ $key ] ) && is_string( $_POST[ $key ] ) ) {
×
228
                                // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: $data is getting sanitized later.
229
                                $data                  = wp_unslash( $_POST[ $key ] );
×
230
                                $new_meta_data[ $key ] = ( $key !== 'wpseo_canonical' ) ? WPSEO_Utils::sanitize_text_field( $data ) : WPSEO_Utils::sanitize_url( $data );
×
231
                        }
232

233
                        // If analysis is disabled remove that analysis score value from the DB.
234
                        if ( $this->is_meta_value_disabled( $key ) ) {
×
235
                                $new_meta_data[ $key ] = '';
×
236
                        }
237
                }
238

239
                // Saving the values.
240
                WPSEO_Taxonomy_Meta::set_values( $term_id, $taxonomy, $new_meta_data );
×
241
        }
242

243
        /**
244
         * Determines if the given meta value key is disabled.
245
         *
246
         * @param string $key The key of the meta value.
247
         * @return bool Whether the given meta value key is disabled.
248
         */
249
        public function is_meta_value_disabled( $key ) {
×
250
                if ( $key === 'wpseo_linkdex' && ! $this->analysis_seo->is_enabled() ) {
×
251
                        return true;
×
252
                }
253

254
                if ( $key === 'wpseo_content_score' && ! $this->analysis_readability->is_enabled() ) {
×
255
                        return true;
×
256
                }
257

258
                if ( $key === 'wpseo_inclusive_language_score' && ! $this->analysis_inclusive_language->is_enabled() ) {
×
259
                        return true;
×
260
                }
261

262
                return false;
×
263
        }
264

265
        /**
266
         * Allows post-kses-filtered HTML in term descriptions.
267
         *
268
         * @return void
269
         */
270
        public function custom_category_descriptions_allow_html() {
×
271
                remove_filter( 'term_description', 'wp_kses_data' );
×
272
                remove_filter( 'pre_term_description', 'wp_filter_kses' );
×
273
                add_filter( 'term_description', 'wp_kses_post' );
×
274
                add_filter( 'pre_term_description', 'wp_filter_post_kses' );
×
275
        }
276

277
        /**
278
         * Output the WordPress editor.
279
         *
280
         * @return void
281
         */
282
        public function custom_category_description_editor() {
×
283
                wp_editor( '', 'description' );
×
284
        }
285

286
        /**
287
         * Pass variables to js for use with the term-scraper.
288
         *
289
         * @param int $term_id The ID of the term to localize the script for.
290
         *
291
         * @return array
292
         */
UNCOV
293
        public function localize_term_scraper_script( $term_id ) {
×
UNCOV
294
                $term     = get_term_by( 'id', $term_id, $this::get_taxonomy() );
×
UNCOV
295
                $taxonomy = get_taxonomy( $term->taxonomy );
×
296

UNCOV
297
                $term_formatter = new WPSEO_Metabox_Formatter(
×
UNCOV
298
                        new WPSEO_Term_Metabox_Formatter( $taxonomy, $term )
×
UNCOV
299
                );
×
300

UNCOV
301
                return $term_formatter->get_values();
×
302
        }
303

304
        /**
305
         * Pass some variables to js for replacing variables.
306
         *
307
         * @return array
308
         */
309
        public function localize_replace_vars_script() {
×
UNCOV
310
                return [
×
311
                        'no_parent_text'           => __( '(no parent)', 'wordpress-seo' ),
×
312
                        'replace_vars'             => $this->get_replace_vars(),
×
313
                        'recommended_replace_vars' => $this->get_recommended_replace_vars(),
×
314
                        'scope'                    => $this->determine_scope(),
×
UNCOV
315
                ];
×
316
        }
317

318
        /**
319
         * Determines the scope based on the current taxonomy.
320
         * This can be used by the replacevar plugin to determine if a replacement needs to be executed.
321
         *
322
         * @return string String decribing the current scope.
323
         */
324
        private function determine_scope() {
×
325
                $taxonomy = $this::get_taxonomy();
×
326

327
                if ( $taxonomy === 'category' ) {
×
328
                        return 'category';
×
329
                }
330

331
                if ( $taxonomy === 'post_tag' ) {
×
332
                        return 'tag';
×
333
                }
334

335
                return 'term';
×
336
        }
337

338
        /**
339
         * Determines if a given page is the term overview page.
340
         *
341
         * @param string $page The string to check for the term overview page.
342
         *
343
         * @return bool
344
         */
UNCOV
345
        public static function is_term_overview( $page ) {
×
UNCOV
346
                return $page === 'edit-tags.php';
×
347
        }
348

349
        /**
350
         * Determines if a given page is the term edit page.
351
         *
352
         * @param string $page The string to check for the term edit page.
353
         *
354
         * @return bool
355
         */
UNCOV
356
        public static function is_term_edit( $page ) {
×
UNCOV
357
                return $page === 'term.php';
×
358
        }
359

360
        /**
361
         * Function to get the labels for the current taxonomy.
362
         *
363
         * @return object|null Labels for the current taxonomy or null if the taxonomy is not set.
364
         */
UNCOV
365
        public static function get_labels() {
×
UNCOV
366
                $term = self::get_taxonomy();
×
UNCOV
367
                if ( $term !== '' ) {
×
UNCOV
368
                        $taxonomy = get_taxonomy( $term );
×
UNCOV
369
                        return $taxonomy->labels;
×
370
                }
UNCOV
371
                return null;
×
372
        }
373

374
        /**
375
         * Retrieves a template.
376
         * Check if metabox for current taxonomy should be displayed.
377
         *
378
         * @return bool
379
         */
380
        private function show_metabox() {
×
381
                $option_key = 'display-metabox-tax-' . $this->taxonomy;
×
382

383
                return WPSEO_Options::get( $option_key );
×
384
        }
385

386
        /**
387
         * Getting the taxonomy from the URL.
388
         *
389
         * @return string
390
         */
391
        private static function get_taxonomy() {
×
392
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
393
                if ( isset( $_GET['taxonomy'] ) && is_string( $_GET['taxonomy'] ) ) {
×
394
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
395
                        return sanitize_text_field( wp_unslash( $_GET['taxonomy'] ) );
×
396
                }
397
                return '';
×
398
        }
399

400
        /**
401
         * Get the current tag ID from the GET parameters.
402
         *
403
         * @return int|null the tag ID if it exists, null otherwise.
404
         */
405
        private static function get_tag_id() {
×
406
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
407
                if ( isset( $_GET['tag_ID'] ) && is_string( $_GET['tag_ID'] ) ) {
×
408
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information, We are casting to an integer.
409
                        $tag_id = (int) wp_unslash( $_GET['tag_ID'] );
×
410
                        if ( $tag_id > 0 ) {
×
411
                                return $tag_id;
×
412
                        }
413
                }
414
                return null;
×
415
        }
416

417
        /**
418
         * Prepares the replace vars for localization.
419
         *
420
         * @return array<string, string> The replacement variables.
421
         */
422
        private function get_replace_vars() {
×
423
                $term_id = $this::get_tag_id();
×
424
                $term    = get_term_by( 'id', $term_id, $this::get_taxonomy() );
×
425

426
                $cached_replacement_vars = [];
×
427

UNCOV
428
                $vars_to_cache = [
×
429
                        'date',
×
UNCOV
430
                        'id',
×
UNCOV
431
                        'sitename',
×
UNCOV
432
                        'sitedesc',
×
UNCOV
433
                        'sep',
×
UNCOV
434
                        'page',
×
UNCOV
435
                        'term_title',
×
UNCOV
436
                        'term_description',
×
UNCOV
437
                        'term_hierarchy',
×
UNCOV
438
                        'category_description',
×
UNCOV
439
                        'tag_description',
×
UNCOV
440
                        'searchphrase',
×
UNCOV
441
                        'currentyear',
×
UNCOV
442
                ];
×
443

444
                foreach ( $vars_to_cache as $var ) {
×
445
                        $cached_replacement_vars[ $var ] = wpseo_replace_vars( '%%' . $var . '%%', $term );
×
446
                }
447

448
                return $cached_replacement_vars;
×
449
        }
450

451
        /**
452
         * Prepares the recommended replace vars for localization.
453
         *
454
         * @return array<string> The recommended replacement variables.
455
         */
456
        private function get_recommended_replace_vars() {
×
457
                $recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
×
458
                $taxonomy                 = $this::get_taxonomy();
×
459

460
                if ( $taxonomy === '' ) {
×
461
                        return [];
×
462
                }
463

464
                // What is recommended depends on the current context.
465
                $page_type = $recommended_replace_vars->determine_for_term( $taxonomy );
×
466

467
                return $recommended_replace_vars->get_recommended_replacevars_for( $page_type );
×
468
        }
469

470
        /**
471
         * Returns an array with shortcode tags for all registered shortcodes.
472
         *
473
         * @return array<string> Array with shortcode tags.
474
         */
475
        private function get_valid_shortcode_tags() {
×
476
                $shortcode_tags = [];
×
477

478
                foreach ( $GLOBALS['shortcode_tags'] as $tag => $description ) {
×
479
                        $shortcode_tags[] = $tag;
×
480
                }
481

482
                return $shortcode_tags;
×
483
        }
484
}
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