PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.7.0
Defender Security – Malware Scanner, Login Security & Firewall v5.7.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / component / class-notification.php

class-notification.php in Defender Security – Malware Scanner, Login Security & Firewall 5.7.0, at src/component/class-notification.php

549 lines 15.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles notifications for various WP Defender components such as Firewall, Malware, and Audit Logging.
4 *
5 * @package WP_Defender\Component
6 */
7
8 namespace WP_Defender\Component;
9
10 use Exception;
11 use WP_User_Query;
12 use WP_Defender\Component;
13 use WP_Defender\Traits\IO;
14 use WP_Defender\Traits\User;
15 use WP_Defender\Behavior\WPMUDEV;
16 use WP_Defender\Model\Setting\Audit_Logging;
17 use WP_Defender\Model\Notification\Audit_Report;
18 use WP_Defender\Model\Notification\Malware_Report;
19 use WP_Defender\Model\Notification\Tweak_Reminder;
20 use WP_Defender\Model\Notification\Firewall_Report;
21 use WP_Defender\Model\Notification\Malware_Notification;
22 use WP_Defender\Model\Notification\Firewall_Notification;
23 use WP_Defender\Controller\Notification as Controller_Notification;
24
25 /**
26 * Handles notifications for various WP Defender components such as Firewall, Malware, and Audit Logging.
27 */
28 class Notification extends Component {
29
30 use User;
31 use IO;
32
33 /**
34 * Indicates if the current installation is a Pro version.
35 *
36 * @var bool
37 */
38 private $is_pro;
39
40 /**
41 * Constructs the Notification component and initializes the WPMUDEV behavior.
42 */
43 public function __construct() {
44 $this->attach_behavior( WPMUDEV::class, WPMUDEV::class );
45 $this->is_pro = ( new WPMUDEV() )->is_pro();
46 }
47
48 /**
49 * Retrieves a pool of users based on specified criteria.
50 *
51 * @param array $exclude User IDs to exclude.
52 * @param string $role Role to filter users by.
53 * @param string $username Search term for username.
54 * @param string $order_by Property to sort by.
55 * @param string $order Sort order (ASC or DESC).
56 * @param int $limit Number of users to retrieve.
57 * @param int $paged Page number for pagination.
58 *
59 * @return array Array of user data including display name, email, role, avatar, and ID.
60 */
61 public function get_users_pool(
62 $exclude = array(),
63 $role = '',
64 $username = '',
65 $order_by = 'ID',
66 $order = 'ASC',
67 $limit = 15,
68 $paged = 1
69 ): array {
70 $params = array(
71 'site_id' => 0,
72 'role' => $role,
73 'orderby' => $order_by,
74 'order' => $order,
75 'number' => $limit,
76 'paged' => $paged,
77 'exclude' => $exclude,
78 );
79
80 if ( ! empty( $username ) ) {
81 $params['search'] = strtolower( $username );
82 $params['search_columns'] = array(
83 'user_login',
84 'user_email',
85 'user_nicename',
86 'display_name',
87 );
88 }
89 $user_query = new WP_User_Query( $params );
90
91 $pools = array();
92 foreach ( $user_query->get_results() as $user ) {
93 $pools[] = array(
94 'name' => $this->get_user_display( $user ),
95 'email' => $this->get_current_user_email( $user ),
96 'role' => $this->get_current_user_role( $user ),
97 'avatar' => get_avatar_url( $this->get_current_user_email( $user ) ),
98 'id' => $user->ID,
99 'status' => \WP_Defender\Model\Notification::USER_SUBSCRIBE_NA,
100 );
101 }
102
103 return $pools;
104 }
105
106 /**
107 * Dispatches notifications based on the module slug and additional arguments.
108 *
109 * @param string $slug Module slug to identify the notification handler.
110 * @param object $args Additional arguments for the notification.
111 */
112 public function dispatch_notification( $slug, $args ) {
113 $module = $this->find_module_by_slug( $slug );
114 if ( is_object( $module ) ) {
115 if ( 'malware-notification' === $module->slug && $module->check_options() ) {
116 // Case report.
117 $module->send( $args );
118 } elseif ( 'firewall-notification' === $module->slug && $module->check_options( $args ) ) {
119 $module->send( $args );
120 }
121 }
122 }
123
124 /**
125 * Finds a notification module by its slug.
126 *
127 * @param string $slug The slug of the module.
128 *
129 * @return mixed Returns the module object if found.
130 */
131 public function find_module_by_slug( $slug ) {
132 switch ( $slug ) {
133 case Tweak_Reminder::SLUG:
134 return wd_di()->get( Tweak_Reminder::class );
135 case Malware_Notification::SLUG:
136 return wd_di()->get( Malware_Notification::class );
137 case Firewall_Notification::SLUG:
138 return wd_di()->get( Firewall_Notification::class );
139 case Malware_Report::SLUG:
140 return wd_di()->get( Malware_Report::class );
141 case Firewall_Report::SLUG:
142 return wd_di()->get( Firewall_Report::class );
143 case Audit_Report::SLUG:
144 default:
145 return wd_di()->get( Audit_Report::class );
146 }
147 }
148
149 /**
150 * Send a verification email to users.
151 *
152 * @param \WP_Defender\Model\Notification $model Notification model containing recipient details.
153 *
154 * @throws Exception Emits Exception in case of an error.
155 */
156 public function send_subscription_confirm_email( \WP_Defender\Model\Notification $model ) {
157 foreach ( $model->in_house_recipients as &$subscriber ) {
158 if ( empty( $subscriber['status'] ) ) {
159 continue;
160 }
161 if ( \WP_Defender\Model\Notification::USER_SUBSCRIBE_NA !== $subscriber['status'] ) {
162 continue;
163 }
164 $ret = $this->send_email( $subscriber, $model );
165
166 if ( $ret ) {
167 $subscriber['status'] = \WP_Defender\Model\Notification::USER_SUBSCRIBE_WAITING;
168 }
169 }
170 foreach ( $model->out_house_recipients as &$subscriber ) {
171 if ( empty( $subscriber['status'] ) ) {
172 continue;
173 }
174 if ( \WP_Defender\Model\Notification::USER_SUBSCRIBE_NA !== $subscriber['status'] ) {
175 continue;
176 }
177 $ret = $this->send_email( $subscriber, $model );
178
179 if ( $ret ) {
180 $subscriber['status'] = \WP_Defender\Model\Notification::USER_SUBSCRIBE_WAITING;
181 }
182 }
183
184 $model->save();
185 }
186
187 /**
188 * Sends an email to a subscriber.
189 *
190 * @param array $subscriber Subscriber information.
191 * @param \WP_Defender\Model\Notification $model Notification model used for the email.
192 *
193 * @return bool Returns true if the email was sent successfully.
194 */
195 public function send_email( $subscriber, \WP_Defender\Model\Notification $model ) {
196 $headers = wd_di()->get( Mail::class )->get_headers(
197 defender_noreply_email( 'wd_confirm_noreply_email' ),
198 'subscription'
199 );
200 $email = $subscriber['email'];
201 $name = $subscriber['name'] ?? '';
202 $inhouse = false;
203 if ( isset( $subscriber['id'] ) ) {
204 $inhouse = true;
205 }
206 $url = $this->create_subscribe_url( $model->slug, $email, $inhouse );
207 $subject = sprintf( /* translators: %s: Model title. */ 'Subscribe to %s', $model->title );
208 // Renders emails.
209 $notification = wd_di()->get( Controller_Notification::class );
210 $content_body = $notification->render_partial(
211 'email/confirm',
212 array(
213 'subject' => $subject,
214 'email' => $email,
215 'notification_name' => $model->title,
216 'url' => $url,
217 'site_url' => network_site_url(),
218 'name' => $name,
219 ),
220 false
221 );
222 $content = $notification->render_partial(
223 'email/index',
224 array(
225 'title' => preg_replace( '/ - Notification$/', '', $model->title ),
226 'content_body' => $content_body,
227 // An empty value because this is a confirmation email.
228 'unsubscribe_link' => '',
229 ),
230 false
231 );
232
233 // We send email here.
234 return wp_mail( $email, $subject, $content, $headers );
235 }
236
237 /**
238 * Sends a subscription confirmation email to a user.
239 *
240 * @param string $email Email address of the subscriber.
241 * @param object $m Notification model object.
242 * @param string $name Name of the subscriber.
243 */
244 public function send_subscribed_email( $email, $m, $name ) {
245 $headers = wd_di()->get( Mail::class )->get_headers(
246 defender_noreply_email( 'wd_subscribe_noreply_email' ),
247 'subscribe_confimed'
248 );
249
250 $notification = wd_di()->get( Controller_Notification::class );
251 $subject = esc_html__( 'Confirmed', 'defender-security' );
252 $content_body = $notification->render_partial(
253 'email/subscribed',
254 array(
255 'subject' => esc_html__( 'Subscription Confirmed', 'defender-security' ),
256 'notification_name' => $m->title,
257 'url' => $this->create_unsubscribe_url( $m->slug, $email ),
258 'name' => $name,
259 )
260 );
261 $content = $notification->render_partial(
262 'email/index',
263 array(
264 'title' => preg_replace( '/ - Notification$/', '', $m->title ),
265 'content_body' => $content_body,
266 // An empty value because this is a subscribed email.
267 'unsubscribe_link' => '',
268 ),
269 false
270 );
271
272 wp_mail( $email, $subject, $content, $headers );
273 }
274
275 /**
276 * Sends an unsubscribe email to a user.
277 *
278 * @param object $m Notification model object.
279 * @param string $email Email address of the subscriber.
280 * @param bool $inhouse Indicates if the user is an in-house user.
281 * @param string $name Name of the subscriber.
282 */
283 public function send_unsubscribe_email( $m, $email, $inhouse, $name ) {
284 $subject = esc_html__( 'Unsubscribed', 'defender-security' );
285 $url = $this->create_subscribe_url( $m->slug, $email, $inhouse );
286 // Render emails.
287 $notification = wd_di()->get( Controller_Notification::class );
288 $content_body = $notification->render_partial(
289 'email/unsubscribe',
290 array(
291 'subject' => esc_html__( 'Unsubscribed', 'defender-security' ),
292 'notification_name' => $m->title,
293 'url' => $url,
294 'name' => $name,
295 )
296 );
297 $title = preg_replace( '/ - Notification$/', '', $m->title );
298 $title = preg_replace( '/ - Reporting$/', '', $title );
299 $content = $notification->render_partial(
300 'email/index',
301 array(
302 'title' => $title,
303 'content_body' => $content_body,
304 // An empty value because this is an unsubscribed email.
305 'unsubscribe_link' => '',
306 ),
307 false
308 );
309
310 $headers = wd_di()->get( Mail::class )->get_headers(
311 defender_noreply_email( 'wd_unsubscribe_noreply_email' ),
312 'unsubscription'
313 );
314
315 wp_mail( $email, $subject, $content, $headers );
316 }
317
318 /**
319 * Create a URL for unsubscribing from notifications.
320 *
321 * @param string $slug Notification slug.
322 * @param string $email Email address of the subscriber.
323 *
324 * @return string Unsubscribe URL.
325 */
326 public function create_unsubscribe_url( $slug, $email ): string {
327 return add_query_arg(
328 array(
329 'action' => Controller_Notification::SLUG_UNSUBSCRIBE,
330 'hash' => hash( 'sha256', $email . AUTH_SALT ),
331 'slug' => $slug,
332 ),
333 admin_url( 'admin-ajax.php' )
334 );
335 }
336
337 /**
338 * Create a URL for subscribing to notifications.
339 *
340 * @param string $slug Notification slug.
341 * @param string $email Email address of the subscriber.
342 * @param bool $inhouse Indicates if the user is an in-house user.
343 *
344 * @return string Subscribe URL.
345 */
346 public function create_subscribe_url( $slug, $email, $inhouse ): string {
347 return add_query_arg(
348 array(
349 'action' => Controller_Notification::SLUG_SUBSCRIBE,
350 'hash' => hash( 'sha256', $email . AUTH_SALT ),
351 'uid' => $slug,
352 'inhouse' => $inhouse,
353 ),
354 admin_url( 'admin-ajax.php' )
355 );
356 }
357
358 /**
359 * Get all modules as array of arrays.
360 *
361 * @return array
362 */
363 public function get_modules(): array {
364 $modules = array(
365 wd_di()->get( Tweak_Reminder::class )->export(),
366 wd_di()->get( Malware_Notification::class )->export(),
367 wd_di()->get( Firewall_Notification::class )->export(),
368 );
369
370 if ( true === $this->is_pro ) {
371 return array_merge( $modules, $this->get_active_pro_reports() );
372 } else {
373 return $modules;
374 }
375 }
376
377 /**
378 * Get all modules as array of objects.
379 *
380 * @return array
381 */
382 public function get_modules_as_objects(): array {
383 $modules = array(
384 wd_di()->get( Tweak_Reminder::class ),
385 wd_di()->get( Malware_Notification::class ),
386 wd_di()->get( Firewall_Notification::class ),
387 );
388
389 if ( true === $this->is_pro ) {
390 $modules = array_merge(
391 $modules,
392 array(
393 wd_di()->get( Malware_Report::class ),
394 wd_di()->get( Firewall_Report::class ),
395 wd_di()->get( Audit_Report::class ),
396 )
397 );
398 }
399
400 return $modules;
401 }
402
403 /**
404 * Return the time that next report will be trigger.
405 *
406 * @return string|null
407 */
408 public function get_next_run() {
409 if ( false === $this->is_pro ) {
410 return esc_html__( 'Never', 'defender-security' );
411 }
412 $modules = $this->get_active_pro_reports_as_objects();
413 $next_run = null;
414 foreach ( $modules as $module ) {
415 if ( \WP_Defender\Model\Notification::STATUS_ACTIVE !== $module->status ) {
416 continue;
417 }
418 if ( is_null( $next_run ) ) {
419 $next_run = $module;
420 } elseif ( $module->est_timestamp < $next_run->est_timestamp ) {
421 $next_run = $module;
422 }
423 }
424 if ( is_null( $next_run ) ) {
425 return esc_html__( 'Never', 'defender-security' );
426 }
427
428 return $next_run->get_next_run_as_string();
429 }
430
431 /**
432 * Get inactive modules.
433 *
434 * @return array
435 * @since 2.7.0 Malware Scanning - Reporting may be inactive.
436 */
437 public function get_inactive_modules(): array {
438 if ( false === $this->is_pro ) {
439 return array();
440 }
441 $modules = array();
442 if ( false === wd_di()->get( \WP_Defender\Model\Setting\Scan::class )->scheduled_scanning ) {
443 $module = wd_di()->get( Malware_Report::class )->export();
444 $module['link'] = network_admin_url( 'admin.php?page=wdf-scan&view=settings&enable=scheduled_scanning#setting_scheduled_scanning' );
445 $modules[] = $module;
446 }
447 if ( false === wd_di()->get( Audit_Logging::class )->is_active() ) {
448 $module = wd_di()->get( Audit_Report::class )->export();
449 $module['link'] = network_admin_url( 'admin.php?page=wdf-logging&view=logs' );
450 $modules[] = $module;
451 }
452
453 return $modules;
454 }
455
456 /**
457 * Get active modules of Pro reports as array of arrays.
458 *
459 * @return array
460 */
461 public function get_active_pro_reports(): array {
462 $modules = array();
463 // Malware_Report.
464 if ( true === wd_di()->get( \WP_Defender\Model\Setting\Scan::class )->scheduled_scanning ) {
465 $modules[] = wd_di()->get( Malware_Report::class )->export();
466 }
467 // Firewall_Report.
468 $modules[] = wd_di()->get( Firewall_Report::class )->export();
469 // Audit_Report.
470 if ( true === wd_di()->get( Audit_Logging::class )->is_active() ) {
471 $modules[] = wd_di()->get( Audit_Report::class )->export();
472 }
473
474 return $modules;
475 }
476
477 /**
478 * Get active modules of Pro reports as array of objects.
479 *
480 * @return array
481 */
482 public function get_active_pro_reports_as_objects(): array {
483 $modules = array();
484 // Malware_Report.
485 if ( true === wd_di()->get( \WP_Defender\Model\Setting\Scan::class )->scheduled_scanning ) {
486 $modules[] = wd_di()->get( Malware_Report::class );
487 }
488 // Firewall_Report.
489 $modules[] = wd_di()->get( Firewall_Report::class );
490 // Audit_Report.
491 if ( true === wd_di()->get( Audit_Logging::class )->is_active() ) {
492 $modules[] = wd_di()->get( Audit_Report::class );
493 }
494
495 return $modules;
496 }
497
498 /**
499 * Counts the number of active modules.
500 *
501 * @return int Number of active modules.
502 */
503 public function count_active(): int {
504 $count = 0;
505 foreach ( $this->get_modules() as $module ) {
506 if ( \WP_Defender\Model\Notification::STATUS_ACTIVE === $module['status'] ) {
507 ++$count;
508 }
509 }
510
511 return $count;
512 }
513
514 /**
515 * Dispatches reports if conditions are met.
516 */
517 public function maybe_dispatch_report() {
518 $modules = array( wd_di()->get( Tweak_Reminder::class ) );
519 if ( true === $this->is_pro ) {
520 $modules = array_merge( $modules, $this->get_active_pro_reports_as_objects() );
521 }
522
523 foreach ( $modules as $module ) {
524 if ( $module->maybe_send() ) {
525 $module->send();
526 }
527 }
528 }
529
530 /**
531 * Get available user roles with user count.
532 *
533 * @return array Return user roles with user count.
534 */
535 public function get_user_roles(): array {
536 $user_roles = count_users();
537
538 if ( isset( $user_roles['avail_roles'] ) ) {
539 foreach ( $user_roles['avail_roles'] as $key => $value ) {
540 if ( 0 === $value ) {
541 unset( $user_roles['avail_roles'][ $key ] );
542 }
543 }
544 }
545
546 return $user_roles;
547 }
548 }
549