• 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
/inc/options/class-wpseo-taxonomy-meta.php
1
<?php
2
/**
3
 * WPSEO plugin file.
4
 *
5
 * @package WPSEO\Internals\Options
6
 */
7

8
/**
9
 * Option: wpseo_taxonomy_meta.
10
 */
11
class WPSEO_Taxonomy_Meta extends WPSEO_Option {
12

13
        /**
14
         * Option name.
15
         *
16
         * @var string
17
         */
18
        public $option_name = 'wpseo_taxonomy_meta';
19

20
        /**
21
         * Whether to include the option in the return for WPSEO_Options::get_all().
22
         *
23
         * @var bool
24
         */
25
        public $include_in_all = false;
26

27
        /**
28
         * Array of defaults for the option.
29
         *
30
         * Shouldn't be requested directly, use $this->get_defaults();
31
         *
32
         * {@internal Important: in contrast to most defaults, the below array format is
33
         *            very bare. The real option is in the format [taxonomy_name][term_id][...]
34
         *            where [...] is any of the $defaults_per_term options shown below.
35
         *            This is of course taken into account in the below methods.}}
36
         *
37
         * @var array
38
         */
39
        protected $defaults = [];
40

41
        /**
42
         * Option name - same as $option_name property, but now also available to static methods.
43
         *
44
         * @var string
45
         */
46
        public static $name;
47

48
        /**
49
         * Array of defaults for individual taxonomy meta entries.
50
         *
51
         * @var array
52
         */
53
        public static $defaults_per_term = [
54
                'wpseo_title'                    => '',
55
                'wpseo_desc'                     => '',
56
                'wpseo_canonical'                => '',
57
                'wpseo_bctitle'                  => '',
58
                'wpseo_noindex'                  => 'default',
59
                'wpseo_focuskw'                  => '',
60
                'wpseo_linkdex'                  => '',
61
                'wpseo_content_score'            => '',
62
                'wpseo_inclusive_language_score' => '',
63
                'wpseo_focuskeywords'            => '[]',
64
                'wpseo_keywordsynonyms'          => '[]',
65
                'wpseo_is_cornerstone'           => '0',
66

67
                // Social fields.
68
                'wpseo_opengraph-title'          => '',
69
                'wpseo_opengraph-description'    => '',
70
                'wpseo_opengraph-image'          => '',
71
                'wpseo_opengraph-image-id'       => '',
72
                'wpseo_twitter-title'            => '',
73
                'wpseo_twitter-description'      => '',
74
                'wpseo_twitter-image'            => '',
75
                'wpseo_twitter-image-id'         => '',
76
        ];
77

78
        /**
79
         * Available index options.
80
         *
81
         * Used for form generation and input validation.
82
         *
83
         * {@internal Labels (translation) added on admin_init via WPSEO_Taxonomy::translate_meta_options().}}
84
         *
85
         * @var array
86
         */
87
        public static $no_index_options = [
88
                'default' => '',
89
                'index'   => '',
90
                'noindex' => '',
91
        ];
92

93
        /**
94
         * Add the actions and filters for the option.
95
         *
96
         * @todo [JRF => testers] Check if the extra actions below would run into problems if an option
97
         * is updated early on and if so, change the call to schedule these for a later action on add/update
98
         * instead of running them straight away.
99
         */
100
        protected function __construct() {
×
101
                parent::__construct();
×
102

103
                self::$name = $this->option_name;
×
104
        }
105

106
        /**
107
         * Get the singleton instance of this class.
108
         *
109
         * @return object
110
         */
111
        public static function get_instance() {
×
112
                if ( ! ( self::$instance instanceof self ) ) {
×
113
                        self::$instance = new self();
×
114
                        self::$name     = self::$instance->option_name;
×
115
                }
116

117
                return self::$instance;
×
118
        }
119

120
        /**
121
         * Add extra default options received from a filter.
122
         *
123
         * @return void
124
         */
125
        public function enrich_defaults() {
×
126
                $extra_defaults_per_term = apply_filters( 'wpseo_add_extra_taxmeta_term_defaults', [] );
×
127
                if ( is_array( $extra_defaults_per_term ) ) {
×
128
                        self::$defaults_per_term = array_merge( $extra_defaults_per_term, self::$defaults_per_term );
×
129
                }
130
        }
131

132
        /**
133
         * Validate the option.
134
         *
135
         * @param array $dirty New value for the option.
136
         * @param array $clean Clean value for the option, normally the defaults.
137
         * @param array $old   Old value of the option.
138
         *
139
         * @return array Validated clean value for the option to be saved to the database.
140
         */
141
        protected function validate_option( $dirty, $clean, $old ) {
×
142
                /*
143
                 * Prevent complete validation (which can be expensive when there are lots of terms)
144
                 * if only one item has changed and has already been validated.
145
                 */
146
                if ( isset( $dirty['wpseo_already_validated'] ) && $dirty['wpseo_already_validated'] === true ) {
×
147
                        unset( $dirty['wpseo_already_validated'] );
×
148

149
                        return $dirty;
×
150
                }
151

152
                foreach ( $dirty as $taxonomy => $terms ) {
×
153
                        /* Don't validate taxonomy - may not be registered yet and we don't want to remove valid ones. */
154
                        if ( is_array( $terms ) && $terms !== [] ) {
×
155
                                foreach ( $terms as $term_id => $meta_data ) {
×
156
                                        /* Only validate term if the taxonomy exists. */
157
                                        if ( taxonomy_exists( $taxonomy ) && get_term_by( 'id', $term_id, $taxonomy ) === false ) {
×
158
                                                /* Is this term id a special case ? */
159
                                                if ( has_filter( 'wpseo_tax_meta_special_term_id_validation_' . $term_id ) !== false ) {
×
160
                                                        $clean[ $taxonomy ][ $term_id ] = apply_filters( 'wpseo_tax_meta_special_term_id_validation_' . $term_id, $meta_data, $taxonomy, $term_id );
×
161
                                                }
162
                                                continue;
×
163
                                        }
164

165
                                        if ( is_array( $meta_data ) && $meta_data !== [] ) {
×
166
                                                /* Validate meta data. */
167
                                                $old_meta  = self::get_term_meta( $term_id, $taxonomy );
×
168
                                                $meta_data = self::validate_term_meta_data( $meta_data, $old_meta );
×
169
                                                if ( $meta_data !== [] ) {
×
170
                                                        $clean[ $taxonomy ][ $term_id ] = $meta_data;
×
171
                                                }
172
                                        }
173

174
                                        // Deal with special cases (for when taxonomy doesn't exist yet).
175
                                        if ( ! isset( $clean[ $taxonomy ][ $term_id ] ) && has_filter( 'wpseo_tax_meta_special_term_id_validation_' . $term_id ) !== false ) {
×
176
                                                $clean[ $taxonomy ][ $term_id ] = apply_filters( 'wpseo_tax_meta_special_term_id_validation_' . $term_id, $meta_data, $taxonomy, $term_id );
×
177
                                        }
178
                                }
179
                        }
180
                }
181

182
                return $clean;
×
183
        }
184

185
        /**
186
         * Validate the meta data for one individual term and removes default values (no need to save those).
187
         *
188
         * @param array $meta_data New values.
189
         * @param array $old_meta  The original values.
190
         *
191
         * @return array Validated and filtered value.
192
         */
UNCOV
193
        public static function validate_term_meta_data( $meta_data, $old_meta ) {
×
194

UNCOV
195
                $clean     = self::$defaults_per_term;
×
UNCOV
196
                $meta_data = array_map( [ 'WPSEO_Utils', 'trim_recursive' ], $meta_data );
×
197

UNCOV
198
                if ( ! is_array( $meta_data ) || $meta_data === [] ) {
×
199
                        return $clean;
×
200
                }
201

UNCOV
202
                foreach ( $clean as $key => $value ) {
×
203
                        switch ( $key ) {
204

UNCOV
205
                                case 'wpseo_noindex':
×
UNCOV
206
                                        if ( isset( $meta_data[ $key ] ) ) {
×
UNCOV
207
                                                if ( isset( self::$no_index_options[ $meta_data[ $key ] ] ) ) {
×
UNCOV
208
                                                        $clean[ $key ] = $meta_data[ $key ];
×
209
                                                }
210
                                        }
211
                                        elseif ( isset( $old_meta[ $key ] ) ) {
×
212
                                                // Retain old value if field currently not in use.
213
                                                $clean[ $key ] = $old_meta[ $key ];
×
214
                                        }
UNCOV
215
                                        break;
×
216

UNCOV
217
                                case 'wpseo_canonical':
×
UNCOV
218
                                        if ( isset( $meta_data[ $key ] ) && $meta_data[ $key ] !== '' ) {
×
UNCOV
219
                                                $url = WPSEO_Utils::sanitize_url( $meta_data[ $key ] );
×
UNCOV
220
                                                if ( $url !== '' ) {
×
UNCOV
221
                                                        $clean[ $key ] = $url;
×
222
                                                }
UNCOV
223
                                                unset( $url );
×
224
                                        }
UNCOV
225
                                        break;
×
226

UNCOV
227
                                case 'wpseo_bctitle':
×
UNCOV
228
                                        if ( isset( $meta_data[ $key ] ) ) {
×
UNCOV
229
                                                $clean[ $key ] = WPSEO_Utils::sanitize_text_field( $meta_data[ $key ] );
×
230
                                        }
231
                                        elseif ( isset( $old_meta[ $key ] ) ) {
×
232
                                                // Retain old value if field currently not in use.
233
                                                $clean[ $key ] = $old_meta[ $key ];
×
234
                                        }
UNCOV
235
                                        break;
×
236

UNCOV
237
                                case 'wpseo_keywordsynonyms':
×
UNCOV
238
                                        if ( isset( $meta_data[ $key ] ) && is_string( $meta_data[ $key ] ) ) {
×
239
                                                // The data is stringified JSON. Use `json_decode` and `json_encode` around the sanitation.
UNCOV
240
                                                $input         = json_decode( $meta_data[ $key ], true );
×
UNCOV
241
                                                $sanitized     = array_map( [ 'WPSEO_Utils', 'sanitize_text_field' ], $input );
×
UNCOV
242
                                                $clean[ $key ] = WPSEO_Utils::format_json_encode( $sanitized );
×
243
                                        }
244
                                        elseif ( isset( $old_meta[ $key ] ) ) {
×
245
                                                // Retain old value if field currently not in use.
246
                                                $clean[ $key ] = $old_meta[ $key ];
×
247
                                        }
UNCOV
248
                                        break;
×
249

UNCOV
250
                                case 'wpseo_focuskeywords':
×
UNCOV
251
                                        if ( isset( $meta_data[ $key ] ) && is_string( $meta_data[ $key ] ) ) {
×
252
                                                // The data is stringified JSON. Use `json_decode` and `json_encode` around the sanitation.
UNCOV
253
                                                $input = json_decode( $meta_data[ $key ], true );
×
254

255
                                                // This data has two known keys: `keyword` and `score`.
UNCOV
256
                                                $sanitized = [];
×
UNCOV
257
                                                foreach ( $input as $entry ) {
×
UNCOV
258
                                                        $sanitized[] = [
×
UNCOV
259
                                                                'keyword' => WPSEO_Utils::sanitize_text_field( $entry['keyword'] ),
×
UNCOV
260
                                                                'score'   => WPSEO_Utils::sanitize_text_field( $entry['score'] ),
×
261
                                                        ];
262
                                                }
263

UNCOV
264
                                                $clean[ $key ] = WPSEO_Utils::format_json_encode( $sanitized );
×
265
                                        }
266
                                        elseif ( isset( $old_meta[ $key ] ) ) {
×
267
                                                // Retain old value if field currently not in use.
268
                                                $clean[ $key ] = $old_meta[ $key ];
×
269
                                        }
UNCOV
270
                                        break;
×
271

UNCOV
272
                                case 'wpseo_focuskw':
×
UNCOV
273
                                case 'wpseo_title':
×
UNCOV
274
                                case 'wpseo_desc':
×
UNCOV
275
                                case 'wpseo_linkdex':
×
276
                                default:
UNCOV
277
                                        if ( isset( $meta_data[ $key ] ) && is_string( $meta_data[ $key ] ) ) {
×
UNCOV
278
                                                $clean[ $key ] = WPSEO_Utils::sanitize_text_field( $meta_data[ $key ] );
×
279
                                        }
280

UNCOV
281
                                        if ( $key === 'wpseo_focuskw' ) {
×
282
                                                $search = [
UNCOV
283
                                                        '&lt;',
×
284
                                                        '&gt;',
285
                                                        '&#96',
286
                                                        '<',
287
                                                        '>',
288
                                                        '`',
289
                                                ];
290

UNCOV
291
                                                $clean[ $key ] = str_replace( $search, '', $clean[ $key ] );
×
292
                                        }
UNCOV
293
                                        break;
×
294
                        }
295

UNCOV
296
                        $clean[ $key ] = apply_filters( 'wpseo_sanitize_tax_meta_' . $key, $clean[ $key ], ( $meta_data[ $key ] ?? null ), ( $old_meta[ $key ] ?? null ) );
×
297
                }
298

299
                // Only save the non-default values.
UNCOV
300
                return array_diff_assoc( $clean, self::$defaults_per_term );
×
301
        }
302

303
        /**
304
         * Clean a given option value.
305
         * - Convert old option values to new
306
         * - Fixes strings which were escaped (should have been sanitized - escaping is for output)
307
         *
308
         * @param array       $option_value          Old (not merged with defaults or filtered) option value to
309
         *                                           clean according to the rules for this option.
310
         * @param string|null $current_version       Optional. Version from which to upgrade, if not set,
311
         *                                           version specific upgrades will be disregarded.
312
         * @param array|null  $all_old_option_values Optional. Only used when importing old options to have
313
         *                                           access to the real old values, in contrast to the saved ones.
314
         *
315
         * @return array Cleaned option.
316
         */
317
        protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
×
318

319
                /* Clean up old values and remove empty arrays. */
320
                if ( is_array( $option_value ) && $option_value !== [] ) {
×
321

322
                        foreach ( $option_value as $taxonomy => $terms ) {
×
323

324
                                if ( is_array( $terms ) && $terms !== [] ) {
×
325

326
                                        foreach ( $terms as $term_id => $meta_data ) {
×
327
                                                if ( ! is_array( $meta_data ) || $meta_data === [] ) {
×
328
                                                        // Remove empty term arrays.
329
                                                        unset( $option_value[ $taxonomy ][ $term_id ] );
×
330
                                                }
331
                                                else {
332
                                                        foreach ( $meta_data as $key => $value ) {
×
333

334
                                                                switch ( $key ) {
335
                                                                        case 'noindex':
×
336
                                                                                if ( $value === 'on' ) {
×
337
                                                                                        // Convert 'on' to 'noindex'.
338
                                                                                        $option_value[ $taxonomy ][ $term_id ][ $key ] = 'noindex';
×
339
                                                                                }
340
                                                                                break;
×
341

342
                                                                        case 'canonical':
×
343
                                                                        case 'wpseo_bctitle':
×
344
                                                                        case 'wpseo_title':
×
345
                                                                        case 'wpseo_desc':
×
346
                                                                        case 'wpseo_linkdex':
×
347
                                                                                // @todo [JRF => whomever] Needs checking, I don't have example data [JRF].
348
                                                                                if ( $value !== '' ) {
×
349
                                                                                        // Fix incorrectly saved (encoded) canonical urls and texts.
350
                                                                                        $option_value[ $taxonomy ][ $term_id ][ $key ] = wp_specialchars_decode( stripslashes( $value ), ENT_QUOTES );
×
351
                                                                                }
352
                                                                                break;
×
353

354
                                                                        default:
355
                                                                                // @todo [JRF => whomever] Needs checking, I don't have example data [JRF].
356
                                                                                if ( $value !== '' ) {
×
357
                                                                                        // Fix incorrectly saved (escaped) text strings.
358
                                                                                        $option_value[ $taxonomy ][ $term_id ][ $key ] = wp_specialchars_decode( $value, ENT_QUOTES );
×
359
                                                                                }
360
                                                                                break;
×
361
                                                                }
362
                                                        }
363
                                                }
364
                                        }
365
                                }
366
                                else {
367
                                        // Remove empty taxonomy arrays.
368
                                        unset( $option_value[ $taxonomy ] );
×
369
                                }
370
                        }
371
                }
372

373
                return $option_value;
×
374
        }
375

376
        /**
377
         * Retrieve a taxonomy term's meta value(s).
378
         *
379
         * @param mixed       $term     Term to get the meta value for
380
         *                              either (string) term name, (int) term id or (object) term.
381
         * @param string      $taxonomy Name of the taxonomy to which the term is attached.
382
         * @param string|null $meta     Optional. Meta value to get (without prefix).
383
         *
384
         * @return mixed Value for the $meta if one is given, might be the default.
385
         *               If no meta is given, an array of all the meta data for the term.
386
         *               False if the term does not exist or the $meta provided is invalid.
387
         */
388
        public static function get_term_meta( $term, $taxonomy, $meta = null ) {
×
389
                /* Figure out the term id. */
390
                if ( is_int( $term ) ) {
×
391
                        $term = get_term_by( 'id', $term, $taxonomy );
×
392
                }
393
                elseif ( is_string( $term ) ) {
×
394
                        $term = get_term_by( 'slug', $term, $taxonomy );
×
395
                }
396

397
                if ( is_object( $term ) && isset( $term->term_id ) ) {
×
398
                        $term_id = $term->term_id;
×
399
                }
400
                else {
401
                        return false;
×
402
                }
403

404
                $tax_meta = self::get_term_tax_meta( $term_id, $taxonomy );
×
405

406
                /*
407
                 * Either return the complete array or a single value from it or false if the value does not exist
408
                 * (shouldn't happen after merge with defaults, indicates typo in request).
409
                 */
410
                if ( ! isset( $meta ) ) {
×
411
                        return $tax_meta;
×
412
                }
413

414
                if ( isset( $tax_meta[ 'wpseo_' . $meta ] ) ) {
×
415
                        return $tax_meta[ 'wpseo_' . $meta ];
×
416
                }
417

418
                return false;
×
419
        }
420

421
        /**
422
         * Get the current queried object and return the meta value.
423
         *
424
         * @param string $meta The meta field that is needed.
425
         *
426
         * @return mixed
427
         */
UNCOV
428
        public static function get_meta_without_term( $meta ) {
×
UNCOV
429
                $term = $GLOBALS['wp_query']->get_queried_object();
×
UNCOV
430
                if ( ! $term || empty( $term->taxonomy ) ) {
×
UNCOV
431
                        return false;
×
432
                }
433

UNCOV
434
                return self::get_term_meta( $term, $term->taxonomy, $meta );
×
435
        }
436

437
        /**
438
         * Saving the values for the given term_id.
439
         *
440
         * @param int    $term_id     ID of the term to save data for.
441
         * @param string $taxonomy    The taxonomy the term belongs to.
442
         * @param array  $meta_values The values that will be saved.
443
         *
444
         * @return void
445
         */
446
        public static function set_values( $term_id, $taxonomy, array $meta_values ) {
×
447
                /* Validate the post values */
448
                $old   = self::get_term_meta( $term_id, $taxonomy );
×
449
                $clean = self::validate_term_meta_data( $meta_values, $old );
×
450

451
                self::save_clean_values( $term_id, $taxonomy, $clean );
×
452
        }
453

454
        /**
455
         * Setting a single value to the term meta.
456
         *
457
         * @param int    $term_id    ID of the term to save data for.
458
         * @param string $taxonomy   The taxonomy the term belongs to.
459
         * @param string $meta_key   The target meta key to store the value in.
460
         * @param string $meta_value The value of the target meta key.
461
         *
462
         * @return void
463
         */
464
        public static function set_value( $term_id, $taxonomy, $meta_key, $meta_value ) {
×
465

466
                if ( substr( strtolower( $meta_key ), 0, 6 ) !== 'wpseo_' ) {
×
467
                        $meta_key = 'wpseo_' . $meta_key;
×
468
                }
469

470
                self::set_values( $term_id, $taxonomy, [ $meta_key => $meta_value ] );
×
471
        }
472

473
        /**
474
         * Find the keyword usages in the metas for the taxonomies/terms.
475
         *
476
         * @param string $keyword          The keyword to look for.
477
         * @param string $current_term_id  The current term id.
478
         * @param string $current_taxonomy The current taxonomy name.
479
         *
480
         * @return array
481
         */
482
        public static function get_keyword_usage( $keyword, $current_term_id, $current_taxonomy ) {
×
483
                $tax_meta = self::get_tax_meta();
×
484

485
                $found = [];
×
486
                // @todo Check for terms of all taxonomies, not only the current taxonomy.
487
                foreach ( $tax_meta as $taxonomy_name => $terms ) {
×
488
                        foreach ( $terms as $term_id => $meta_values ) {
×
489
                                $is_current = ( $current_taxonomy === $taxonomy_name && (string) $current_term_id === (string) $term_id );
×
490
                                if ( ! $is_current && ! empty( $meta_values['wpseo_focuskw'] ) && $meta_values['wpseo_focuskw'] === $keyword ) {
×
491
                                        $found[] = $term_id;
×
492
                                }
493
                        }
494
                }
495

496
                return [ $keyword => $found ];
×
497
        }
498

499
        /**
500
         * Saving the values for the given term_id.
501
         *
502
         * @param int    $term_id  ID of the term to save data for.
503
         * @param string $taxonomy The taxonomy the term belongs to.
504
         * @param array  $clean    Array with clean values.
505
         *
506
         * @return void
507
         */
508
        private static function save_clean_values( $term_id, $taxonomy, array $clean ) {
×
509
                $tax_meta = self::get_tax_meta();
×
510

511
                /* Add/remove the result to/from the original option value. */
512
                if ( $clean !== [] ) {
×
513
                        $tax_meta[ $taxonomy ][ $term_id ] = $clean;
×
514
                }
515
                else {
516
                        unset( $tax_meta[ $taxonomy ][ $term_id ] );
×
517
                        if ( isset( $tax_meta[ $taxonomy ] ) && $tax_meta[ $taxonomy ] === [] ) {
×
518
                                unset( $tax_meta[ $taxonomy ] );
×
519
                        }
520
                }
521

522
                // Prevent complete array validation.
523
                $tax_meta['wpseo_already_validated'] = true;
×
524

525
                self::save_tax_meta( $tax_meta );
×
526
        }
527

528
        /**
529
         * Getting the meta from the options.
530
         *
531
         * @return void|array
532
         */
533
        private static function get_tax_meta() {
×
534
                return get_option( self::$name );
×
535
        }
536

537
        /**
538
         * Saving the tax meta values to the database.
539
         *
540
         * @param array $tax_meta Array with the meta values for taxonomy.
541
         *
542
         * @return void
543
         */
544
        private static function save_tax_meta( $tax_meta ) {
×
545
                update_option( self::$name, $tax_meta );
×
546
        }
547

548
        /**
549
         * Getting the taxonomy meta for the given term_id and taxonomy.
550
         *
551
         * @param int    $term_id  The id of the term.
552
         * @param string $taxonomy Name of the taxonomy to which the term is attached.
553
         *
554
         * @return array
555
         */
556
        private static function get_term_tax_meta( $term_id, $taxonomy ) {
×
557
                $tax_meta = self::get_tax_meta();
×
558

559
                /* If we have data for the term, merge with defaults for complete array, otherwise set defaults. */
560
                if ( isset( $tax_meta[ $taxonomy ][ $term_id ] ) ) {
×
561
                        return array_merge( self::$defaults_per_term, $tax_meta[ $taxonomy ][ $term_id ] );
×
562
                }
563

564
                return self::$defaults_per_term;
×
565
        }
566
}
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