PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.1
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.1
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / admin / class-yoast-notification.php

class-yoast-notification.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.1, at admin/class-yoast-notification.php

417 lines 9.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 const MATCH_ALL = 'all';
20
21 /**
22 * Type of capability check.
23 *
24 * @var string
25 */
26 const MATCH_ANY = 'any';
27
28 /**
29 * Notification type.
30 *
31 * @var string
32 */
33 const ERROR = 'error';
34
35 /**
36 * Notification type.
37 *
38 * @var string
39 */
40 const WARNING = 'warning';
41
42 /**
43 * Notification type.
44 *
45 * @var string
46 */
47 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 *
63 * @var array
64 */
65 private $options = [];
66
67 /**
68 * Contains default values for the optional arguments.
69 *
70 * @var array
71 */
72 private $defaults = [
73 'type' => self::UPDATED,
74 'id' => '',
75 'user' => null,
76 'nonce' => null,
77 'priority' => 0.5,
78 'data_json' => [],
79 'dismissal_key' => null,
80 'capabilities' => [],
81 'capability_check' => self::MATCH_ALL,
82 'yoast_branding' => false,
83 ];
84
85 /**
86 * The message for the notification.
87 *
88 * @var string
89 */
90 private $message;
91
92 /**
93 * Notification class constructor.
94 *
95 * @param string $message Message string.
96 * @param array $options Set of options.
97 */
98 public function __construct( $message, $options = [] ) {
99 $this->message = $message;
100 $this->options = $this->normalize_options( $options );
101 }
102
103 /**
104 * Retrieve notification ID string.
105 *
106 * @return string
107 */
108 public function get_id() {
109 return $this->options['id'];
110 }
111
112 /**
113 * Retrieve the user to show the notification for.
114 *
115 * @return WP_User The user to show this notification for.
116 */
117 public function get_user() {
118 return $this->options['user'];
119 }
120
121 /**
122 * Retrieve the is of the user to show the notification for.
123 *
124 * Returns the id of the current user if not user has been sent.
125 *
126 * @return int The user id
127 */
128 public function get_user_id() {
129 if ( $this->get_user() !== null ) {
130 return $this->get_user()->ID;
131 }
132 return get_current_user_id();
133 }
134
135 /**
136 * Retrieve nonce identifier.
137 *
138 * @return string|null Nonce for this Notification.
139 */
140 public function get_nonce() {
141 if ( $this->options['id'] && empty( $this->options['nonce'] ) ) {
142 $this->options['nonce'] = wp_create_nonce( $this->options['id'] );
143 }
144
145 return $this->options['nonce'];
146 }
147
148 /**
149 * Make sure the nonce is up to date.
150 */
151 public function refresh_nonce() {
152 if ( $this->options['id'] ) {
153 $this->options['nonce'] = wp_create_nonce( $this->options['id'] );
154 }
155 }
156
157 /**
158 * Get the type of the notification.
159 *
160 * @return string
161 */
162 public function get_type() {
163 return $this->options['type'];
164 }
165
166 /**
167 * Priority of the notification.
168 *
169 * Relative to the type.
170 *
171 * @return float Returns the priority between 0 and 1.
172 */
173 public function get_priority() {
174 return $this->options['priority'];
175 }
176
177 /**
178 * Get the User Meta key to check for dismissal of notification.
179 *
180 * @return string User Meta Option key that registers dismissal.
181 */
182 public function get_dismissal_key() {
183 if ( empty( $this->options['dismissal_key'] ) ) {
184 return $this->options['id'];
185 }
186
187 return $this->options['dismissal_key'];
188 }
189
190 /**
191 * Is this Notification persistent.
192 *
193 * @return bool True if persistent, False if fire and forget.
194 */
195 public function is_persistent() {
196 $id = $this->get_id();
197
198 return ! empty( $id );
199 }
200
201 /**
202 * Check if the notification is relevant for the current user.
203 *
204 * @return bool True if a user needs to see this notification, false if not.
205 */
206 public function display_for_current_user() {
207 // If the notification is for the current page only, always show.
208 if ( ! $this->is_persistent() ) {
209 return true;
210 }
211
212 // If the current user doesn't match capabilities.
213 return $this->match_capabilities();
214 }
215
216 /**
217 * Does the current user match required capabilities.
218 *
219 * @return bool
220 */
221 public function match_capabilities() {
222 // Super Admin can do anything.
223 if ( is_multisite() && is_super_admin( $this->options['user']->ID ) ) {
224 return true;
225 }
226
227 /**
228 * Filter capabilities that enable the displaying of this notification.
229 *
230 * @param array $capabilities The capabilities that must be present for this notification.
231 * @param Yoast_Notification $notification The notification object.
232 *
233 * @return array Array of capabilities or empty for no restrictions.
234 *
235 * @since 3.2
236 */
237 $capabilities = apply_filters( 'wpseo_notification_capabilities', $this->options['capabilities'], $this );
238
239 // Should be an array.
240 if ( ! is_array( $capabilities ) ) {
241 $capabilities = (array) $capabilities;
242 }
243
244 /**
245 * Filter capability check to enable all or any capabilities.
246 *
247 * @param string $capability_check The type of check that will be used to determine if an capability is present.
248 * @param Yoast_Notification $notification The notification object.
249 *
250 * @return string self::MATCH_ALL or self::MATCH_ANY.
251 *
252 * @since 3.2
253 */
254 $capability_check = apply_filters( 'wpseo_notification_capability_check', $this->options['capability_check'], $this );
255
256 if ( ! in_array( $capability_check, [ self::MATCH_ALL, self::MATCH_ANY ], true ) ) {
257 $capability_check = self::MATCH_ALL;
258 }
259
260 if ( ! empty( $capabilities ) ) {
261
262 $has_capabilities = array_filter( $capabilities, [ $this, 'has_capability' ] );
263
264 switch ( $capability_check ) {
265 case self::MATCH_ALL:
266 return $has_capabilities === $capabilities;
267 case self::MATCH_ANY:
268 return ! empty( $has_capabilities );
269 }
270 }
271
272 return true;
273 }
274
275 /**
276 * Array filter function to find matched capabilities.
277 *
278 * @param string $capability Capability to test.
279 *
280 * @return bool
281 */
282 private function has_capability( $capability ) {
283 $user = $this->options['user'];
284 return $user->has_cap( $capability );
285 }
286
287 /**
288 * Return the object properties as an array.
289 *
290 * @return array
291 */
292 public function to_array() {
293 return [
294 'message' => $this->message,
295 'options' => $this->options,
296 ];
297 }
298
299 /**
300 * Adds string (view) behaviour to the notification.
301 *
302 * @return string
303 */
304 public function __toString() {
305 return $this->render();
306 }
307
308 /**
309 * Renders the notification as a string.
310 *
311 * @return string The rendered notification.
312 */
313 public function render() {
314 $attributes = [];
315
316 // Default notification classes.
317 $classes = [
318 'yoast-notification',
319 ];
320
321 // Maintain WordPress visualisation of notifications when they are not persistent.
322 if ( ! $this->is_persistent() ) {
323 $classes[] = 'notice';
324 $classes[] = $this->get_type();
325 }
326
327 if ( ! empty( $classes ) ) {
328 $attributes['class'] = implode( ' ', $classes );
329 }
330
331 // Combined attribute key and value into a string.
332 array_walk( $attributes, [ $this, 'parse_attributes' ] );
333
334 $message = null;
335 if ( $this->options['yoast_branding'] ) {
336 $message = $this->wrap_yoast_seo_icon( $this->message );
337 }
338
339 if ( $message === null ) {
340 $message = wpautop( $this->message );
341 }
342
343 // Build the output DIV.
344 return '<div ' . implode( ' ', $attributes ) . '>' . $message . '</div>' . PHP_EOL;
345 }
346
347 /**
348 * Wraps the message with a Yoast SEO icon.
349 *
350 * @param string $message The message to wrap.
351 *
352 * @return string The wrapped message.
353 */
354 private function wrap_yoast_seo_icon( $message ) {
355 $out = sprintf(
356 '<img src="%1$s" height="%2$d" width="%3$d" class="yoast-seo-icon" />',
357 esc_url( plugin_dir_url( WPSEO_FILE ) . 'packages/js/images/Yoast_SEO_Icon.svg' ),
358 60,
359 60
360 );
361 $out .= '<div class="yoast-seo-icon-wrap">';
362 $out .= $message;
363 $out .= '</div>';
364
365 return $out;
366 }
367
368 /**
369 * Get the JSON if provided.
370 *
371 * @return false|string
372 */
373 public function get_json() {
374 if ( empty( $this->options['data_json'] ) ) {
375 return '';
376 }
377
378 return WPSEO_Utils::format_json_encode( $this->options['data_json'] );
379 }
380
381 /**
382 * Make sure we only have values that we can work with.
383 *
384 * @param array $options Options to normalize.
385 *
386 * @return array
387 */
388 private function normalize_options( $options ) {
389 $options = wp_parse_args( $options, $this->defaults );
390
391 // Should not exceed 0 or 1.
392 $options['priority'] = min( 1, max( 0, $options['priority'] ) );
393
394 // Set default capabilities when not supplied.
395 if ( empty( $options['capabilities'] ) || $options['capabilities'] === [] ) {
396 $options['capabilities'] = [ 'wpseo_manage_options' ];
397 }
398
399 // Set to the current user if not supplied.
400 if ( $options['user'] === null ) {
401 $options['user'] = wp_get_current_user();
402 }
403
404 return $options;
405 }
406
407 /**
408 * Format HTML element attributes.
409 *
410 * @param string $value Attribute value.
411 * @param string $key Attribute name.
412 */
413 private function parse_attributes( &$value, $key ) {
414 $value = sprintf( '%s="%s"', sanitize_key( $key ), esc_attr( $value ) );
415 }
416 }
417