PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.3
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.3
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 19.1 All 128 releases
wordpress-seo / admin / class-yoast-notifications.php

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

320 lines 7.6 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 */
7
8 /**
9 * Class Yoast_Notifications.
10 */
11 class Yoast_Notifications {
12
13 /**
14 * Holds the admin page's ID.
15 *
16 * @var string
17 */
18 public const ADMIN_PAGE = 'wpseo_dashboard';
19
20 /**
21 * Total notifications count.
22 *
23 * @var int
24 */
25 private static $notification_count = 0;
26
27 /**
28 * All error notifications.
29 *
30 * @var array
31 */
32 private static $errors = [];
33
34 /**
35 * Active errors.
36 *
37 * @var array
38 */
39 private static $active_errors = [];
40
41 /**
42 * Dismissed errors.
43 *
44 * @var array
45 */
46 private static $dismissed_errors = [];
47
48 /**
49 * All warning notifications.
50 *
51 * @var array
52 */
53 private static $warnings = [];
54
55 /**
56 * Active warnings.
57 *
58 * @var array
59 */
60 private static $active_warnings = [];
61
62 /**
63 * Dismissed warnings.
64 *
65 * @var array
66 */
67 private static $dismissed_warnings = [];
68
69 /**
70 * Yoast_Notifications constructor.
71 */
72 public function __construct() {
73
74 $this->add_hooks();
75 }
76
77 /**
78 * Add hooks
79 *
80 * @return void
81 */
82 private function add_hooks() {
83 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
84 if ( isset( $_GET['page'] ) && is_string( $_GET['page'] ) ) {
85 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
86 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) );
87 if ( $page === self::ADMIN_PAGE ) {
88 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
89 }
90 }
91
92 // Needed for adminbar and Notifications page.
93 add_action( 'admin_init', [ self::class, 'collect_notifications' ], 99 );
94
95 // Add AJAX hooks.
96 add_action( 'wp_ajax_yoast_dismiss_notification', [ $this, 'ajax_dismiss_notification' ] );
97 add_action( 'wp_ajax_yoast_restore_notification', [ $this, 'ajax_restore_notification' ] );
98 }
99
100 /**
101 * Enqueue assets.
102 *
103 * @return void
104 */
105 public function enqueue_assets() {
106 $asset_manager = new WPSEO_Admin_Asset_Manager();
107
108 $asset_manager->enqueue_style( 'notifications' );
109 }
110
111 /**
112 * Handle ajax request to dismiss a notification.
113 *
114 * @return void
115 */
116 public function ajax_dismiss_notification() {
117
118 $notification = $this->get_notification_from_ajax_request();
119 if ( $notification ) {
120 $notification_center = Yoast_Notification_Center::get();
121 $notification_center->maybe_dismiss_notification( $notification );
122
123 $this->output_ajax_response( $notification->get_type() );
124 }
125
126 wp_die();
127 }
128
129 /**
130 * Handle ajax request to restore a notification.
131 *
132 * @return void
133 */
134 public function ajax_restore_notification() {
135
136 $notification = $this->get_notification_from_ajax_request();
137 if ( $notification ) {
138 $notification_center = Yoast_Notification_Center::get();
139 $notification_center->restore_notification( $notification );
140
141 $this->output_ajax_response( $notification->get_type() );
142 }
143
144 wp_die();
145 }
146
147 /**
148 * Create AJAX response data.
149 *
150 * @param string $type Notification type.
151 *
152 * @return void
153 */
154 private function output_ajax_response( $type ) {
155
156 $html = $this->get_view_html( $type );
157 // phpcs:disable WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe.
158 echo WPSEO_Utils::format_json_encode(
159 [
160 'html' => $html,
161 'total' => self::get_active_notification_count(),
162 ],
163 );
164 // phpcs:enable -- Reason: WPSEO_Utils::format_json_encode is safe.
165 }
166
167 /**
168 * Get the HTML to return in the AJAX request.
169 *
170 * @param string $type Notification type.
171 *
172 * @return bool|string
173 */
174 private function get_view_html( $type ) {
175
176 switch ( $type ) {
177 case 'error':
178 $view = 'errors';
179 break;
180
181 case 'warning':
182 default:
183 $view = 'warnings';
184 break;
185 }
186
187 // Re-collect notifications.
188 self::collect_notifications();
189
190 /**
191 * Stops PHPStorm from nagging about this variable being unused. The variable is used in the view.
192 *
193 * @noinspection PhpUnusedLocalVariableInspection
194 */
195 $notifications_data = self::get_template_variables();
196
197 ob_start();
198 include WPSEO_PATH . 'admin/views/partial-notifications-' . $view . '.php';
199 $html = ob_get_clean();
200
201 return $html;
202 }
203
204 /**
205 * Extract the Yoast Notification from the AJAX request.
206 *
207 * This function does not handle nonce verification.
208 *
209 * @return Yoast_Notification|null A Yoast_Notification on success, null on failure.
210 */
211 private function get_notification_from_ajax_request() {
212 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: This function does not handle nonce verification.
213 if ( ! isset( $_POST['notification'] ) || ! is_string( $_POST['notification'] ) ) {
214 return null;
215 }
216 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: This function does not handle nonce verification.
217 $notification_id = sanitize_text_field( wp_unslash( $_POST['notification'] ) );
218
219 if ( empty( $notification_id ) ) {
220 return null;
221 }
222 $notification_center = Yoast_Notification_Center::get();
223 return $notification_center->get_notification_by_id( $notification_id );
224 }
225
226 /**
227 * Collect the notifications and group them together.
228 *
229 * @return void
230 */
231 public static function collect_notifications() {
232
233 $notification_center = Yoast_Notification_Center::get();
234
235 $notifications = $notification_center->get_sorted_notifications();
236 self::$notification_count = count( $notifications );
237
238 self::$errors = array_filter( $notifications, [ self::class, 'filter_error_notifications' ] );
239 self::$dismissed_errors = array_filter( self::$errors, [ self::class, 'filter_dismissed_notifications' ] );
240 self::$active_errors = array_diff( self::$errors, self::$dismissed_errors );
241
242 self::$warnings = array_filter( $notifications, [ self::class, 'filter_warning_notifications' ] );
243 self::$dismissed_warnings = array_filter( self::$warnings, [ self::class, 'filter_dismissed_notifications' ] );
244 self::$active_warnings = array_diff( self::$warnings, self::$dismissed_warnings );
245 }
246
247 /**
248 * Get the variables needed in the views.
249 *
250 * @return array
251 */
252 public static function get_template_variables() {
253
254 return [
255 'metrics' => [
256 'total' => self::$notification_count,
257 'active' => self::get_active_notification_count(),
258 'errors' => count( self::$errors ),
259 'warnings' => count( self::$warnings ),
260 ],
261 'errors' => [
262 'dismissed' => self::$dismissed_errors,
263 'active' => self::$active_errors,
264 ],
265 'warnings' => [
266 'dismissed' => self::$dismissed_warnings,
267 'active' => self::$active_warnings,
268 ],
269 ];
270 }
271
272 /**
273 * Get the number of active notifications.
274 *
275 * @return int
276 */
277 public static function get_active_notification_count() {
278
279 return ( count( self::$active_errors ) + count( self::$active_warnings ) );
280 }
281
282 /**
283 * Filter out any non-errors.
284 *
285 * @param Yoast_Notification $notification Notification to test.
286 *
287 * @return bool
288 */
289 private static function filter_error_notifications( Yoast_Notification $notification ) {
290
291 return $notification->get_type() === 'error';
292 }
293
294 /**
295 * Filter out any non-warnings.
296 *
297 * @param Yoast_Notification $notification Notification to test.
298 *
299 * @return bool
300 */
301 private static function filter_warning_notifications( Yoast_Notification $notification ) {
302
303 return $notification->get_type() !== 'error';
304 }
305
306 /**
307 * Filter out any dismissed notifications.
308 *
309 * @param Yoast_Notification $notification Notification to test.
310 *
311 * @return bool
312 */
313 private static function filter_dismissed_notifications( Yoast_Notification $notification ) {
314
315 return Yoast_Notification_Center::is_notification_dismissed( $notification );
316 }
317 }
318
319 class_alias( Yoast_Notifications::class, 'Yoast_Alerts' );
320