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

Yoast / wordpress-seo / ff7fbe548e11c03015095d039b28d1168b751857

16 Feb 2026 01:09AM UTC coverage: 52.986%. Remained the same
ff7fbe548e11c03015095d039b28d1168b751857

push

github

web-flow
Merge pull request #22987 from Yoast/JRF/modernize-use-trailing-commas-in-function-calls

Modernize: use trailing comma in multi-line function call

8482 of 15955 branches covered (53.16%)

Branch coverage included in aggregate %.

396 of 875 new or added lines in 311 files covered. (45.26%)

21 existing lines in 12 files now uncovered.

32462 of 61318 relevant lines covered (52.94%)

48791.32 hits per line

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

55.88
/admin/class-yoast-notification.php
1
<?php
2
/**
3
 * WPSEO plugin file.
4
 *
5
 * @package WPSEO\Admin\Notifications
6
 * @since   1.5.3
7
 */
8

9
/**
10
 * Implements individual notification.
11
 */
12
class Yoast_Notification {
13

14
        /**
15
         * Type of capability check.
16
         *
17
         * @var string
18
         */
19
        public const MATCH_ALL = 'all';
20

21
        /**
22
         * Type of capability check.
23
         *
24
         * @var string
25
         */
26
        public const MATCH_ANY = 'any';
27

28
        /**
29
         * Notification type.
30
         *
31
         * @var string
32
         */
33
        public const ERROR = 'error';
34

35
        /**
36
         * Notification type.
37
         *
38
         * @var string
39
         */
40
        public const WARNING = 'warning';
41

42
        /**
43
         * Notification type.
44
         *
45
         * @var string
46
         */
47
        public const UPDATED = 'updated';
48

49
        /**
50
         * Options of this Notification.
51
         *
52
         * Contains optional arguments:
53
         *
54
         * -             type: The notification type, i.e. 'updated' or 'error'
55
         * -               id: The ID of the notification
56
         * -            nonce: Security nonce to use in case of dismissible notice.
57
         * -         priority: From 0 to 1, determines the order of Notifications.
58
         * -    dismissal_key: Option name to save dismissal information in, ID will be used if not supplied.
59
         * -     capabilities: Capabilities that a user must have for this Notification to show.
60
         * - capability_check: How to check capability pass: all or any.
61
         * -  wpseo_page_only: Only display on wpseo page or on every page.
62
         * -   yoast_branding: Whether to show the Yoast SEO branding in the notification.
63
         * -    resolve_nonce: Security nonce to use in case of resolving the notification.
64
         *
65
         * @var array
66
         */
67
        private $options = [];
68

69
        /**
70
         * Contains default values for the optional arguments.
71
         *
72
         * @var array
73
         */
74
        private $defaults = [
75
                'type'             => self::UPDATED,
76
                'id'               => '',
77
                'user_id'          => null,
78
                'nonce'            => null,
79
                'priority'         => 0.5,
80
                'data_json'        => [],
81
                'dismissal_key'    => null,
82
                'capabilities'     => [],
83
                'capability_check' => self::MATCH_ALL,
84
                'yoast_branding'   => false,
85
                'resolve_nonce'    => '',
86
        ];
87

88
        /**
89
         * The message for the notification.
90
         *
91
         * @var string
92
         */
93
        private $message;
94

95
        /**
96
         * Notification class constructor.
97
         *
98
         * @param string $message Message string.
99
         * @param array  $options Set of options.
100
         */
101
        public function __construct( $message, $options = [] ) {
64✔
102
                $this->message = $message;
64✔
103
                $this->options = $this->normalize_options( $options );
64✔
104
        }
105

106
        /**
107
         * Retrieve notification ID string.
108
         *
109
         * @return string
110
         */
111
        public function get_id() {
28✔
112
                return $this->options['id'];
28✔
113
        }
114

115
        /**
116
         * Retrieve the user to show the notification for.
117
         *
118
         * @deprecated 21.6
119
         * @codeCoverageIgnore
120
         *
121
         * @return WP_User|null The user to show this notification for.
122
         */
123
        public function get_user() {
124
                _deprecated_function( __METHOD__, 'Yoast SEO 21.6' );
125
                return null;
126
        }
127

128
        /**
129
         * Retrieve the id of the user to show the notification for.
130
         *
131
         * Returns the id of the current user if not user has been sent.
132
         *
133
         * @return int The user id
134
         */
135
        public function get_user_id() {
×
136
                return ( $this->options['user_id'] ?? get_current_user_id() );
×
137
        }
138

139
        /**
140
         * Retrieve nonce identifier.
141
         *
142
         * @return string|null Nonce for this Notification.
143
         */
144
        public function get_nonce() {
8✔
145
                if ( $this->options['id'] && empty( $this->options['nonce'] ) ) {
8✔
146
                        $this->options['nonce'] = wp_create_nonce( $this->options['id'] );
4✔
147
                }
148

149
                return $this->options['nonce'];
8✔
150
        }
151

152
        /**
153
         * Make sure the nonce is up to date.
154
         *
155
         * @return void
156
         */
157
        public function refresh_nonce() {
×
158
                if ( $this->options['id'] ) {
×
159
                        $this->options['nonce'] = wp_create_nonce( $this->options['id'] );
×
160
                }
161
        }
162

163
        /**
164
         * Get the type of the notification.
165
         *
166
         * @return string
167
         */
168
        public function get_type() {
8✔
169
                return $this->options['type'];
8✔
170
        }
171

172
        /**
173
         * Priority of the notification.
174
         *
175
         * Relative to the type.
176
         *
177
         * @return float Returns the priority between 0 and 1.
178
         */
179
        public function get_priority() {
4✔
180
                return $this->options['priority'];
4✔
181
        }
182

183
        /**
184
         * Get the nonce to resolve the alert.
185
         *
186
         * @return string
187
         */
188
        public function get_resolve_nonce() {
×
189
                return $this->options['resolve_nonce'];
×
190
        }
191

192
        /**
193
         * Get the User Meta key to check for dismissal of notification.
194
         *
195
         * @return string User Meta Option key that registers dismissal.
196
         */
197
        public function get_dismissal_key() {
8✔
198
                if ( empty( $this->options['dismissal_key'] ) ) {
8✔
199
                        return $this->options['id'];
4✔
200
                }
201

202
                return $this->options['dismissal_key'];
4✔
203
        }
204

205
        /**
206
         * Is this Notification persistent.
207
         *
208
         * @return bool True if persistent, False if fire and forget.
209
         */
210
        public function is_persistent() {
28✔
211
                $id = $this->get_id();
28✔
212

213
                return ! empty( $id );
28✔
214
        }
215

216
        /**
217
         * Check if the notification is relevant for the current user.
218
         *
219
         * @return bool True if a user needs to see this notification, false if not.
220
         */
221
        public function display_for_current_user() {
24✔
222
                // If the notification is for the current page only, always show.
223
                if ( ! $this->is_persistent() ) {
24✔
224
                        return true;
4✔
225
                }
226

227
                // If the current user doesn't match capabilities.
228
                return $this->match_capabilities();
20✔
229
        }
230

231
        /**
232
         * Does the current user match required capabilities.
233
         *
234
         * @return bool
235
         */
236
        public function match_capabilities() {
20✔
237
                // Super Admin can do anything.
238
                if ( is_multisite() && is_super_admin( $this->options['user_id'] ) ) {
20✔
239
                        return true;
×
240
                }
241

242
                /**
243
                 * Filter capabilities that enable the displaying of this notification.
244
                 *
245
                 * @param array              $capabilities The capabilities that must be present for this notification.
246
                 * @param Yoast_Notification $notification The notification object.
247
                 *
248
                 * @return array Array of capabilities or empty for no restrictions.
249
                 *
250
                 * @since 3.2
251
                 */
252
                $capabilities = apply_filters( 'wpseo_notification_capabilities', $this->options['capabilities'], $this );
20✔
253

254
                // Should be an array.
255
                if ( ! is_array( $capabilities ) ) {
20✔
256
                        $capabilities = (array) $capabilities;
4✔
257
                }
258

259
                /**
260
                 * Filter capability check to enable all or any capabilities.
261
                 *
262
                 * @param string             $capability_check The type of check that will be used to determine if an capability is present.
263
                 * @param Yoast_Notification $notification     The notification object.
264
                 *
265
                 * @return string self::MATCH_ALL or self::MATCH_ANY.
266
                 *
267
                 * @since 3.2
268
                 */
269
                $capability_check = apply_filters( 'wpseo_notification_capability_check', $this->options['capability_check'], $this );
20✔
270

271
                if ( ! in_array( $capability_check, [ self::MATCH_ALL, self::MATCH_ANY ], true ) ) {
20✔
272
                        $capability_check = self::MATCH_ALL;
×
273
                }
274

275
                if ( ! empty( $capabilities ) ) {
20✔
276

277
                        $has_capabilities = array_filter( $capabilities, [ $this, 'has_capability' ] );
20✔
278

279
                        switch ( $capability_check ) {
280
                                case self::MATCH_ALL:
281
                                        return $has_capabilities === $capabilities;
12✔
282
                                case self::MATCH_ANY:
283
                                        return ! empty( $has_capabilities );
8✔
284
                        }
285
                }
286

287
                return true;
×
288
        }
289

290
        /**
291
         * Array filter function to find matched capabilities.
292
         *
293
         * @param string $capability Capability to test.
294
         *
295
         * @return bool
296
         */
297
        private function has_capability( $capability ) {
20✔
298
                $user_id = $this->options['user_id'];
20✔
299
                if ( ! is_numeric( $user_id ) ) {
20✔
300
                        return false;
×
301
                }
302
                $user = get_user_by( 'id', $user_id );
20✔
303
                if ( ! $user ) {
20✔
304
                        return false;
×
305
                }
306

307
                return $user->has_cap( $capability );
20✔
308
        }
309

310
        /**
311
         * Return the object properties as an array.
312
         *
313
         * @return array
314
         */
315
        public function to_array() {
8✔
316
                return [
8✔
317
                        'message' => $this->message,
8✔
318
                        'options' => $this->options,
8✔
319
                ];
8✔
320
        }
321

322
        /**
323
         * Adds string (view) behaviour to the notification.
324
         *
325
         * @return string
326
         */
327
        public function __toString() {
×
328
                return $this->render();
×
329
        }
330

331
        /**
332
         * Renders the notification as a string.
333
         *
334
         * @return string The rendered notification.
335
         */
336
        public function render() {
×
337
                $attributes = [];
×
338

339
                // Default notification classes.
340
                $classes = [
×
341
                        'yoast-notification',
×
342
                ];
×
343

344
                // Maintain WordPress visualisation of notifications when they are not persistent.
345
                if ( ! $this->is_persistent() ) {
×
346
                        $classes[] = 'notice';
×
347
                        $classes[] = $this->get_type();
×
348
                }
349

350
                if ( ! empty( $classes ) ) {
×
351
                        $attributes['class'] = implode( ' ', $classes );
×
352
                }
353

354
                // Combined attribute key and value into a string.
355
                array_walk( $attributes, [ $this, 'parse_attributes' ] );
×
356

357
                $message = null;
×
358
                if ( $this->options['yoast_branding'] ) {
×
359
                        $message = $this->wrap_yoast_seo_icon( $this->message );
×
360
                }
361

362
                $message ??= wpautop( $this->message );
×
363

364
                // Build the output DIV.
365
                return '<div ' . implode( ' ', $attributes ) . '>' . $message . '</div>' . PHP_EOL;
×
366
        }
367

368
        /**
369
         * Get the message for the notification.
370
         *
371
         * @return string The message.
372
         */
373
        public function get_message() {
×
374
                return wpautop( $this->message );
×
375
        }
376

377
        /**
378
         * Wraps the message with a Yoast SEO icon.
379
         *
380
         * @param string $message The message to wrap.
381
         *
382
         * @return string The wrapped message.
383
         */
384
        private function wrap_yoast_seo_icon( $message ) {
×
385
                $out  = sprintf(
×
386
                        '<img src="%1$s" height="%2$d" width="%3$d" class="yoast-seo-icon" />',
×
387
                        esc_url( plugin_dir_url( WPSEO_FILE ) . 'packages/js/images/Yoast_SEO_Icon.svg' ),
×
388
                        60,
×
NEW
389
                        60,
×
390
                );
×
391
                $out .= '<div class="yoast-seo-icon-wrap">';
×
392
                $out .= $message;
×
393
                $out .= '</div>';
×
394

395
                return $out;
×
396
        }
397

398
        /**
399
         * Get the JSON if provided.
400
         *
401
         * @return string|false
402
         */
403
        public function get_json() {
4✔
404
                if ( empty( $this->options['data_json'] ) ) {
4✔
405
                        return '';
4✔
406
                }
407

408
                return WPSEO_Utils::format_json_encode( $this->options['data_json'] );
4✔
409
        }
410

411
        /**
412
         * Make sure we only have values that we can work with.
413
         *
414
         * @param array $options Options to normalize.
415
         *
416
         * @return array
417
         */
418
        private function normalize_options( $options ) {
64✔
419
                $options = wp_parse_args( $options, $this->defaults );
64✔
420

421
                // Should not exceed 0 or 1.
422
                $options['priority'] = min( 1, max( 0, $options['priority'] ) );
64✔
423

424
                // Set default capabilities when not supplied.
425
                if ( empty( $options['capabilities'] ) || $options['capabilities'] === [] ) {
64✔
426
                        $options['capabilities'] = [ 'wpseo_manage_options' ];
44✔
427
                }
428

429
                // Set to the id of the current user if not supplied.
430
                $options['user_id'] ??= get_current_user_id();
64✔
431

432
                return $options;
64✔
433
        }
434

435
        /**
436
         * Format HTML element attributes.
437
         *
438
         * @param string $value Attribute value.
439
         * @param string $key   Attribute name.
440
         *
441
         * @return void
442
         */
443
        private function parse_attributes( &$value, $key ) {
×
444
                $value = sprintf( '%s="%s"', sanitize_key( $key ), esc_attr( $value ) );
×
445
        }
446
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc