PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.0.1
Defender Security – Malware Scanner, Login Security & Firewall v6.0.1
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 / controller / class-notification.php

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

962 lines 26.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles notification operations.
4 *
5 * @package WP_Defender\Controller
6 */
7
8 namespace WP_Defender\Controller;
9
10 use Exception;
11 use WP_Defender\Event;
12 use Calotes\Helper\HTTP;
13 use WP_Defender\Traits\User;
14 use Calotes\Component\Request;
15 use WP_Defender\Traits\Formats;
16 use Calotes\Component\Response;
17 use WP_Defender\Component\Network_Cron_Manager;
18 use WP_Defender\Model\Notification\Audit_Report;
19 use WP_Defender\Model\Notification\Malware_Report;
20 use WP_Defender\Model\Notification\Tweak_Reminder;
21 use WP_Defender\Model\Notification\Firewall_Report;
22 use WP_Defender\Component\Config\Config_Hub_Helper;
23 use WP_Defender\Model\Notification as Model_Notification;
24
25 /**
26 * Methods for handling notifications.
27 */
28 class Notification extends Event {
29
30 use User;
31 use Formats;
32
33 /**
34 * Slug identifier for subscribe page.
35 *
36 * @var string
37 */
38 public const SLUG_SUBSCRIBE = 'defender_listen_user_subscribe';
39
40 /**
41 * Slug identifier for unsubscribe page.
42 *
43 * @var string
44 */
45 public const SLUG_UNSUBSCRIBE = 'defender_listen_user_unsubscribe';
46 /**
47 * The slug identifier for this controller.
48 *
49 * @var string
50 */
51 public $slug = 'wdf-notification';
52
53 /**
54 * Service for handling logic.
55 *
56 * @var \WP_Defender\Component\Notification
57 */
58 protected $service;
59
60 /**
61 * Initializes the model and service, registers routes, and sets up scheduled events if the model is active.
62 */
63 public function __construct() {
64 $this->register_routes();
65 $this->service = wd_di()->get( \WP_Defender\Component\Notification::class );
66 add_action( 'defender_enqueue_assets', array( $this, 'enqueue_assets' ) );
67 // We use custom ajax endpoint here as the nonce would fail with other user.
68 add_action( 'wp_ajax_' . self::SLUG_UNSUBSCRIBE, array( $this, 'unsubscribe_and_send_email' ) );
69 add_action( 'wp_ajax_nopriv_' . self::SLUG_UNSUBSCRIBE, array( $this, 'unsubscribe_and_send_email' ) );
70 add_action( 'defender_notify', array( $this, 'send_notify' ), 10, 2 );
71 add_action( 'admin_notices', array( $this, 'show_actions_with_subscription' ) );
72 }
73
74 /**
75 * For users who have subscribed or unsubscribed confirmation.
76 *
77 * @return void
78 */
79 public function show_actions_with_subscription() {
80 if ( ! defined( 'IS_PROFILE_PAGE' ) || false === constant( 'IS_PROFILE_PAGE' ) ) {
81 return;
82 }
83 $slug = defender_get_data_from_request( 'slug', 'g' );
84 if ( ! is_string( $slug ) || '' === trim( $slug ) ) {
85 return;
86 }
87 $m = $this->service->find_module_by_slug( $slug );
88 if ( ! is_object( $m ) ) {
89 return;
90 }
91 $context = defender_get_data_from_request( 'context', 'g' );
92 if ( 'subscribed' === $context ) {
93 $unsubscribe_link = $this->service->create_unsubscribe_url( $m->slug, $this->get_current_user_email() );
94 $strings = sprintf(
95 /* translators: 1. Module title. 2. Unsubscribed link. */
96 esc_html__(
97 'You are now subscribed to receive %1$s. Made a mistake? %2$s',
98 'defender-security'
99 ),
100 '<strong>' . $m->title . '</strong>',
101 '<a href="' . esc_url_raw( $unsubscribe_link ) . '" style="text-decoration: none">' . esc_html__( 'Unsubscribe', 'defender-security' ) . '</a>'
102 );
103 } elseif ( 'unsubscribe' === $context ) {
104 $strings = sprintf(
105 /* translators: %s: Module title. */
106 esc_html__( 'You are now unsubscribed from %s.', 'defender-security' ),
107 '<strong>' . $m->title . '</strong>'
108 );
109 } else {
110 return;
111 }
112 ?>
113 <div class="notice notice-success" style="position:relative;">
114 <p><?php echo wp_kses_post( $strings ); ?></p>
115 <a href="<?php echo esc_url_raw( get_edit_profile_url() ); ?>" class="notice-dismiss"
116 style="text-decoration: none">
117 <span class="screen-reader-text"><?php esc_attr_e( 'Dismiss this notice.', 'defender-security' ); ?></span>
118 </a>
119 </div>
120 <?php
121 }
122
123 /**
124 * Renders the main view for this page.
125 */
126 public function main_view() {
127 $this->render( 'main' );
128 }
129
130 /**
131 * Dispatch notification.
132 *
133 * @param string $slug Module slug to identify the notification handler.
134 * @param object $args The arguments to pass to the notification.
135 */
136 public function send_notify( $slug, $args ) {
137 $this->service->dispatch_notification( $slug, $args );
138 }
139
140 /**
141 * Validates an email address provided in the request data.
142 *
143 * @param Request $request The request object .The request object containing the data to validate.
144 *
145 * @return Response The response object indicating the validation result.
146 * @defender_route
147 */
148 public function validate_email( Request $request ): Response {
149 $data = $request->get_data(
150 array(
151 'email' => array(
152 'type' => 'string',
153 'sanitize' => 'sanitize_text_field',
154 ),
155 )
156 );
157 $email = $data['email'] ?? false;
158 if ( filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
159 return new Response(
160 true,
161 array(
162 'error' => false,
163 'avatar' => get_avatar_url( $data['email'] ),
164 )
165 );
166 } else {
167 return new Response( false, array( 'error' => esc_html__( 'Invalid email address.', 'defender-security' ) ) );
168 }
169 }
170
171 /**
172 * Updates a WordPress user profile for an in-house recipient.
173 *
174 * @param Request $request The request object.
175 *
176 * @return Response The response object.
177 * @defender_route
178 */
179 public function update_user_profile( Request $request ): Response {
180 $data = $request->get_data(
181 array(
182 'id' => array(
183 'type' => 'int',
184 'sanitize' => 'sanitize_text_field',
185 ),
186 'first_name' => array(
187 'type' => 'string',
188 'sanitize' => 'sanitize_text_field',
189 ),
190 'last_name' => array(
191 'type' => 'string',
192 'sanitize' => 'sanitize_text_field',
193 ),
194 'display_name' => array(
195 'type' => 'string',
196 'sanitize' => 'sanitize_text_field',
197 ),
198 )
199 );
200
201 $user_id = absint( $data['id'] ?? 0 );
202 $display_name = trim( (string) ( $data['display_name'] ?? '' ) );
203
204 if ( 0 === $user_id || '' === $display_name ) {
205 return new Response( false, array( 'message' => esc_html__( 'Invalid user data.', 'defender-security' ) ) );
206 }
207
208 if ( ! current_user_can( 'edit_user', $user_id ) ) {
209 return new Response( false, array( 'message' => esc_html__( 'You are not allowed to edit this user.', 'defender-security' ) ) );
210 }
211
212 if ( ! get_user_by( 'id', $user_id ) ) {
213 return new Response( false, array( 'message' => esc_html__( 'User not found.', 'defender-security' ) ) );
214 }
215
216 $result = wp_update_user(
217 array(
218 'ID' => $user_id,
219 'first_name' => trim( (string) ( $data['first_name'] ?? '' ) ),
220 'last_name' => trim( (string) ( $data['last_name'] ?? '' ) ),
221 'display_name' => $display_name,
222 )
223 );
224
225 if ( is_wp_error( $result ) ) {
226 return new Response( false, array( 'message' => $result->get_error_message() ) );
227 }
228
229 return new Response(
230 true,
231 array(
232 'id' => $user_id,
233 'first_name' => trim( (string) ( $data['first_name'] ?? '' ) ),
234 'last_name' => trim( (string) ( $data['last_name'] ?? '' ) ),
235 'display_name' => $display_name,
236 )
237 );
238 }
239
240 /**
241 * Unsubscribe process.
242 */
243 public function unsubscribe_and_send_email() {
244 $slug = HTTP::get( 'slug', '' );
245 $hash = HTTP::get( 'hash', '' );
246 $slug = sanitize_text_field( $slug );
247 if ( ! is_string( $slug ) || '' === trim( $slug ) || ! is_string( $hash ) || '' === trim( $hash ) ) {
248 wp_die( esc_html__( 'You shall not pass.', 'defender-security' ) );
249 }
250 $m = $this->service->find_module_by_slug( $slug );
251 if ( ! is_object( $m ) ) {
252 wp_die( esc_html__( 'You shall not pass.', 'defender-security' ) );
253 }
254 $inhouse = false;
255 foreach ( $m->in_house_recipients as &$recipient ) {
256 $email = $recipient['email'];
257 if ( hash_equals( $hash, hash( 'sha256', $email . AUTH_SALT ) ) ) {
258 // We skip even an un-logged user, because the admin can change the user's access without notice.
259 if ( is_user_logged_in() ) {
260 if ( $email !== $this->get_current_user_email() ) {
261 wp_die( esc_html__( 'Invalid request.', 'defender-security' ) );
262 }
263 $inhouse = true;
264 }
265 $recipient['status'] = Model_Notification::USER_SUBSCRIBE_CANCELED;
266 $m->save();
267 // Send email.
268 $this->service->send_unsubscribe_email( $m, $email, $inhouse, $recipient['name'] );
269 break;
270 }
271 }
272
273 if ( false === $inhouse ) {
274 // No match on in-house, check the outhouse list.
275 foreach ( $m->out_house_recipients as &$recipient ) {
276 $email = $recipient['email'];
277 if ( hash_equals( $hash, hash( 'sha256', $email . AUTH_SALT ) ) ) {
278 $recipient['status'] = Model_Notification::USER_SUBSCRIBE_CANCELED;
279 $m->save();
280 $this->service->send_unsubscribe_email( $m, $email, $inhouse, $recipient['name'] );
281 }
282 }
283 }
284 if ( $inhouse ) {
285 wp_safe_redirect(
286 add_query_arg(
287 array(
288 'slug' => $slug,
289 'context' => 'unsubscribe',
290 ),
291 get_edit_profile_url()
292 )
293 );
294 } else {
295 wp_safe_redirect( get_home_url() );
296 }
297 exit;
298 }
299
300 /**
301 * An endpoint for saving single config from frontend.
302 *
303 * @param Request $request The request object .The request object containing the data to save.
304 *
305 * @defender_route
306 * @return Response
307 * @throws Exception Emits Exception in case of an error.
308 */
309 public function save( Request $request ): Response {
310 $raw_data = $request->get_data();
311 if ( ! isset( $raw_data['slug'] ) || ! is_string( $raw_data['slug'] ) || '' === trim( $raw_data['slug'] ) ) {
312 return new Response( false, array( 'message' => esc_html__( 'Invalid data.', 'defender-security' ) ) );
313 }
314 $slug = sanitize_textarea_field( $raw_data['slug'] );
315 $model = $this->service->find_module_by_slug( $slug );
316
317 if ( ! is_object( $model ) ) {
318 return new Response( false, array( 'message' => esc_html__( 'Invalid data.', 'defender-security' ) ) );
319 }
320 $data = $request->get_data_by_model( $model );
321 // Check config-values.
322 $data['configs'] = $model->type_casting( $data['configs'] );
323
324 $model->import( $data );
325 // Ensure the model is activated. The save route is called when the user explicitly
326 // saves settings, so the notification must be active after saving. If the imported
327 // data already carries an active status this is a no-op; otherwise we activate it.
328 if ( \WP_Defender\Model\Notification::STATUS_ACTIVE !== $model->status ) {
329 $model->status = \WP_Defender\Model\Notification::STATUS_ACTIVE;
330 }
331 if ( $model->validate() ) {
332 if ( 0 === $model->last_sent ) {
333 // This means that the notification or report never sent, we will use the moment that it get activate.
334 $model->last_sent = time();
335 }
336 $model->save();
337 $this->service->send_subscription_confirm_email( $model );
338 Config_Hub_Helper::set_clear_active_flag();
339 // Track.
340 if ( $this->is_tracking_active() ) {
341 $track_data = array( 'Notification type' => $raw_data['title'] );
342 // For reports. Separated check for 'Security Recommendations - Notification'.
343 if ( 'report' === $raw_data['type'] ) {
344 $track_data['Notification schedule'] = ucfirst( $data['frequency'] );
345 } elseif ( 'tweak-reminder' === $raw_data['slug'] ) {
346 $track_data['Notification schedule'] = ucfirst( $data['configs']['reminder'] );
347 }
348 $this->track_feature( 'def_notification_activated', $track_data );
349 }
350
351 return new Response(
352 true,
353 array_merge(
354 array(
355 'message' => esc_html__(
356 'You have activated the notification successfully. Note, recipients will need to confirm their subscriptions to begin receiving notifications.',
357 'defender-security'
358 ),
359 ),
360 $this->data_frontend()
361 )
362 );
363 }
364
365 return new Response( false, array( 'message' => $model->get_formatted_errors() ) );
366 }
367
368 /**
369 * Bulk update and save changes.
370 *
371 * @param Request $request The request object.
372 *
373 * @defender_route
374 * @return Response
375 */
376 public function save_bulk( Request $request ): Response {
377 $data = $request->get_data(
378 array(
379 'items' => array(
380 'type' => 'array',
381 'sanitize' => 'sanitize_textarea_field',
382 ),
383 )
384 );
385 $this->save_items( $data['items'] );
386 Config_Hub_Helper::set_clear_active_flag();
387
388 return new Response(
389 true,
390 array_merge(
391 $this->data_frontend(),
392 array(
393 'message' => esc_html__(
394 'Your settings have been updated successfully. Any new recipients will receive an email to confirm their subscription.',
395 'defender-security'
396 ),
397 )
398 )
399 );
400 }
401
402 /**
403 * Sync the shared reports schedule to notification models that persist their own next-run timestamp.
404 *
405 * @param Request $request The request object.
406 *
407 * @defender_route
408 * @return Response
409 */
410 public function sync_report_schedule( Request $request ): Response {
411 $data = $request->get_data(
412 array(
413 'frequency' => array(
414 'type' => 'string',
415 'sanitize' => 'sanitize_text_field',
416 ),
417 'day' => array(
418 'type' => 'string',
419 'sanitize' => 'sanitize_text_field',
420 ),
421 'day_n' => array(
422 'type' => 'int',
423 'sanitize' => 'sanitize_text_field',
424 ),
425 'time' => array(
426 'type' => 'string',
427 'sanitize' => 'sanitize_text_field',
428 ),
429 )
430 );
431
432 $report_classes = array(
433 Firewall_Report::class,
434 Audit_Report::class,
435 Malware_Report::class,
436 Tweak_Reminder::class,
437 );
438
439 foreach ( $report_classes as $report_class ) {
440 $model = wd_di()->get( $report_class );
441 $model->frequency = $data['frequency'];
442 $model->day = $data['day'];
443 $model->day_n = (int) $data['day_n'];
444 $model->time = $data['time'];
445
446 $model->save();
447 }
448
449 Config_Hub_Helper::set_clear_active_flag();
450
451 return new Response( true, $this->data_frontend() );
452 }
453
454 /**
455 * Process bulk saving of reports or notifications.
456 *
457 * @param array $data Data to save.
458 *
459 * @throws Exception Emits Exception in case of an error.
460 */
461 private function save_items( array $data ): void {
462 $in_house_recipients = $data['in_house_recipients'] ?? array();
463 $out_house_recipients = $data['out_house_recipients'] ?? array();
464
465 foreach ( $data as $datum ) {
466 if ( ! is_array( $datum ) || ! isset( $datum['slug'] ) ) {
467 continue;
468 }
469
470 $slug = $datum['slug'];
471 $model = $this->service->find_module_by_slug( $slug );
472 if ( ! is_object( $model ) ) {
473 continue;
474 }
475
476 $import = array(
477 'status' => $model->status,
478 'configs' => $model->type_casting( $datum ),
479 'in_house_recipients' => $datum['in_house_recipients'] ?? $in_house_recipients,
480 'out_house_recipients' => $datum['out_house_recipients'] ?? $out_house_recipients,
481 );
482 // since 2.7.0.
483 if ( isset( $datum['is_report'] ) && (bool) $datum['is_report'] && Malware_Report::SLUG !== $slug ) {
484 $import['frequency'] = $datum['frequency'] ?? $model->frequency;
485 $import['day_n'] = (int) ( $datum['day_n'] ?? $model->day_n );
486 $import['day'] = $datum['day'] ?? $model->day;
487 $import['time'] = $datum['time'] ?? $model->time;
488 }
489 foreach ( $import['out_house_recipients'] as $key => $val ) {
490 if ( ! filter_var( $val['email'], FILTER_VALIDATE_EMAIL ) ) {
491 unset( $import['out_house_recipients'][ $key ] );
492 }
493 }
494 $prev_in_house_emails = array_column( $model->in_house_recipients, null, 'email' );
495 $prev_out_house_emails = array_column( $model->out_house_recipients, null, 'email' );
496
497 $model->import( $import );
498 if ( $model->validate() ) {
499 if ( 0 === $model->last_sent ) {
500 $model->last_sent = time();
501 }
502 $model->save();
503
504 $new_in_house_emails = array_column( $import['in_house_recipients'], 'email' );
505 $new_out_house_emails = array_column( $import['out_house_recipients'], 'email' );
506 foreach ( $prev_in_house_emails as $email => $recipient ) {
507 if ( ! in_array( $email, $new_in_house_emails, true ) ) {
508 $this->service->send_unsubscribe_email( $model, $email, true, $recipient['name'] ?? '' );
509 }
510 }
511 foreach ( $prev_out_house_emails as $email => $recipient ) {
512 if ( ! in_array( $email, $new_out_house_emails, true ) ) {
513 $this->service->send_unsubscribe_email( $model, $email, false, $recipient['name'] ?? '' );
514 }
515 }
516 // Track.
517 if ( $this->is_tracking_active() ) {
518 $track_data = array( 'Notification type' => $model->title );
519 if ( isset( $datum['is_report'] ) && (bool) $datum['is_report'] ) {
520 $track_data['Notification schedule'] = 'tweak-reminder' === $slug
521 ? ucfirst( $datum['reminder'] ?? '' )
522 : ucfirst( $datum['frequency'] ?? '' );
523 }
524 $this->track_feature( 'def_notification_activated', $track_data );
525 }
526 }
527 }
528 }
529
530 /**
531 * Bulk activate.
532 *
533 * @param Request $request The request object.
534 *
535 * @defender_route
536 * @return Response
537 * @throws Exception Emits Exception in case of an error.
538 */
539 public function bulk_activate( Request $request ): Response {
540 $data = $request->get_data(
541 array(
542 'slugs' => array(
543 'type' => 'array',
544 'sanitize' => 'sanitize_text_field',
545 ),
546 )
547 );
548 $slugs = $data['slugs'];
549 if ( ! is_array( $slugs ) || array() === $slugs ) {
550 return new Response( false, array() );
551 }
552
553 foreach ( $slugs as $slug ) {
554 $model = $this->service->find_module_by_slug( $slug );
555 if ( is_object( $model ) ) {
556 $model->status = Model_Notification::STATUS_ACTIVE;
557 if ( 0 === $model->last_sent ) {
558 // This means that the notification or report never sent, we will use the moment that it get activate.
559 $model->last_sent = time();
560 }
561 $model->save();
562 }
563 }
564
565 return new Response(
566 true,
567 array_merge(
568 array(
569 'message' => 'You have activated the notification successfully. Note, recipients will need to confirm their subscriptions to begin receiving notifications.',
570 ),
571 $this->data_frontend()
572 )
573 );
574 }
575
576 /**
577 * Bulk deactivate.
578 *
579 * @param Request $request The request object.
580 *
581 * @defender_route
582 * @return Response
583 */
584 public function bulk_deactivate( Request $request ): Response {
585 $data = $request->get_data(
586 array(
587 'slugs' => array(
588 'type' => 'array',
589 'sanitize' => 'sanitize_text_field',
590 ),
591 )
592 );
593 $slugs = $data['slugs'];
594 if ( ! is_array( $slugs ) || array() === $slugs ) {
595 return new Response( false, array() );
596 }
597
598 foreach ( $slugs as $slug ) {
599 $model = $this->service->find_module_by_slug( $slug );
600 if ( is_object( $model ) ) {
601 $model->status = Model_Notification::STATUS_DISABLED;
602 $model->save();
603 }
604 }
605
606 return new Response(
607 true,
608 array_merge(
609 array( 'message' => esc_html__( 'You have deactivated the notifications successfully.', 'defender-security' ) ),
610 $this->data_frontend()
611 )
612 );
613 }
614
615 /**
616 * Disable a notification module.
617 *
618 * @param Request $request The request object .The request object containing the data to disable the module.
619 *
620 * @defender_route
621 * @return Response The response object indicating the success or failure of the operation.
622 */
623 public function disable( Request $request ): Response {
624 $data = $request->get_data(
625 array(
626 'slug' => array(
627 'type' => 'string',
628 'sanitize' => 'sanitize_text_field',
629 ),
630 )
631 );
632
633 $slug = $data['slug'];
634 $model = $this->service->find_module_by_slug( $slug );
635 if ( ! is_object( $model ) ) {
636 return new Response( false, array( 'message' => esc_html__( 'Invalid data.', 'defender-security' ) ) );
637 }
638
639 $model->status = Model_Notification::STATUS_DISABLED;
640 $model->save();
641
642 return new Response(
643 true,
644 array_merge(
645 $this->data_frontend(),
646 array(
647 'message' => esc_html__( 'You have deactivated the notification successfully.', 'defender-security' ),
648 )
649 )
650 );
651 }
652
653 /**
654 * Enqueues scripts and styles for this page.
655 * Only enqueues assets if the page is active.
656 */
657 public function enqueue_assets() {
658 if ( ! $this->is_page_active() ) {
659 return;
660 }
661 // todo: add changes when the design is ready.
662 }
663
664 /**
665 * An endpoint for fetching users pool.
666 *
667 * @param Request $request The request object .Request data.
668 *
669 * @defender_route
670 */
671 public function get_users( Request $request ) {
672 $data = $request->get_data(
673 array(
674 'paged' => array(
675 'type' => 'int',
676 'sanitize' => 'sanitize_text_field',
677 ),
678 'search' => array(
679 'type' => 'string',
680 'sanitize' => 'sanitize_text_field',
681 ),
682 'exclude' => array(
683 'type' => 'array',
684 ),
685 'module' => array(
686 'type' => 'string',
687 'sanitize' => 'sanitize_text_field',
688 ),
689 'user_role_filter' => array(
690 'type' => 'string',
691 'sanitize' => 'sanitize_text_field',
692 ),
693 'user_sort' => array(
694 'type' => 'string',
695 'sanitize' => 'sanitize_text_field',
696 ),
697 )
698 );
699 $paged = 1;
700 $exclude = $data['exclude'] ?? array();
701 $username = $data['search'] ?? '';
702 $slug = $data['module'] ?? null;
703 $role = '';
704
705 if (
706 isset( $data['user_role_filter'] ) &&
707 'all' !== $data['user_role_filter']
708 ) {
709 $role = $data['user_role_filter'];
710 }
711
712 $order_by = 'ID';
713 $order = 'DESC';
714 if ( isset( $data['user_sort'] ) ) {
715 switch ( $data['user_sort'] ) {
716 case 'recent':
717 $order_by = 'registered';
718 $order = 'DESC';
719 break;
720 case 'alpha_asc':
721 $order_by = 'display_name';
722 $order = 'ASC';
723 break;
724 case 'alpha_desc':
725 default:
726 $order_by = 'display_name';
727 $order = 'DESC';
728 break;
729 }
730 }
731
732 if ( strlen( $username ) ) {
733 $username = "*$username*";
734 }
735
736 $users = $this->service->get_users_pool(
737 $exclude,
738 $role,
739 $username,
740 $order_by,
741 $order,
742 10,
743 $paged
744 );
745
746 if ( ! is_null( $slug ) ) {
747 $notification = $this->service->find_module_by_slug( $slug );
748 if ( is_object( $notification ) ) {
749 foreach ( $notification->in_house_recipients as $recipient ) {
750 foreach ( $users as &$user ) {
751 if ( $user['email'] === $recipient['email'] ) {
752 $user['status'] = $recipient['status'];
753 }
754 }
755 }
756 }
757 }
758
759 wp_send_json_success( $users );
760 }
761
762 /**
763 * Removes settings for all submodules.
764 */
765 public function remove_settings() {
766 foreach ( $this->service->get_modules_as_objects() as $module ) {
767 $module->delete();
768 }
769 }
770
771 /**
772 * Delete all the data & the cache.
773 */
774 public function remove_data() {
775 }
776
777 /**
778 * Converts the current object state to an array.
779 *
780 * @return array The array representation of the object.
781 */
782 public function to_array(): array {
783 return array();
784 }
785
786 /**
787 * Todo: add changes when the design is ready.
788 * Provides data for the frontend.
789 *
790 * @return array An array of data for the frontend.
791 */
792 public function data_frontend(): array {
793 return array_merge(
794 array(
795 'alert_model' => $this->service->get_alert_model(),
796 'reports_model' => $this->service->get_reports_model(),
797 'active_count' => $this->service->count_active(),
798 'hub_connector' => wd_di()->get( Hub_Connector::class )->data_frontend(),
799 'antibot' => wd_di()->get( Antibot_Global_Firewall::class )->data_frontend(),
800 ),
801 $this->dump_routes_and_nonces()
802 );
803 }
804
805 /**
806 * Imports data into the model.
807 *
808 * @param array $data Data to be imported into the model.
809 */
810 public function import_data( array $data ) {
811 }
812
813 /**
814 * Exports strings.
815 *
816 * @return array An array of strings.
817 */
818 public function export_strings(): array {
819 $modules = wd_di()->get( self::class )->service->get_modules_as_objects();
820 $strings = array();
821 foreach ( $modules as $module ) {
822 /* translators: %s - module title, %s - module status */
823 $string = esc_html__( '%1$s: %2$s', 'defender-security' );
824 if ( 'notification' === $module->type ) {
825 $string = sprintf(
826 $string,
827 $module->title,
828 Model_Notification::STATUS_ACTIVE === $module->status ? esc_html__(
829 'Enabled',
830 'defender-security'
831 ) : esc_html__( 'Disabled', 'defender-security' )
832 );
833 } else {
834 $string = sprintf(
835 $string,
836 $module->title,
837 Model_Notification::STATUS_ACTIVE === $module->status ? $module->to_string() : esc_html__(
838 'Disabled',
839 'defender-security'
840 )
841 );
842 }
843 $strings[] = $string;
844 }
845
846 return $strings;
847 }
848
849 /**
850 * Resend invite email.
851 *
852 * @param Request $request The request object.
853 *
854 * @defender_route
855 * @return Response
856 */
857 public function resend_invite_email( Request $request ): Response {
858 $data = $request->get_data(
859 array(
860 'slug' => array(
861 'type' => 'string',
862 'sanitize' => 'sanitize_textarea_field',
863 ),
864 'email' => array(
865 'type' => 'string',
866 'sanitize' => 'sanitize_text_field',
867 ),
868 'id' => array(
869 'type' => 'integer',
870 ),
871 'name' => array(
872 'type' => 'string',
873 'sanitize' => 'sanitize_text_field',
874 ),
875 )
876 );
877
878 $model = $this->service->find_module_by_slug( $data['slug'] );
879
880 if ( ! is_object( $model ) ) {
881 return new Response( false, array( 'message' => esc_html__( 'Module not found.', 'defender-security' ) ) );
882 }
883
884 $subscriber = array(
885 'email' => $data['email'],
886 'name' => $data['name'],
887 );
888
889 if ( isset( $data['id'] ) && is_int( $data['id'] ) && 0 < $data['id'] ) {
890 $subscriber['id'] = $data['id'];
891 }
892 // Resend invite email now.
893 $sent = $this->service->send_email( $subscriber, $model->export() );
894
895 if ( $sent ) {
896 return new Response( true, array( 'message' => esc_html__( 'Invitation sent successfully.', 'defender-security' ) ) );
897 }
898
899 return new Response(
900 false,
901 array(
902 'message' => esc_html__( 'Sorry! We could not send the invitation, Please try again later.', 'defender-security' ),
903 )
904 );
905 }
906
907 /**
908 * Get user roles with count.
909 *
910 * @defender_route
911 */
912 public function get_user_roles() {
913 $user_roles = $this->service->get_user_roles();
914
915 wp_send_json_success( $user_roles );
916 }
917
918 /**
919 * Sends a single invite email listing all notification modules the recipient was added to.
920 *
921 * @param Request $request Request with 'email' and 'name'.
922 * @defender_route
923 * @return Response
924 */
925 public function send_invite_all_email( Request $request ): Response {
926 $data = $request->get_data(
927 array(
928 'email' => array(
929 'type' => 'string',
930 'sanitize' => 'sanitize_email',
931 ),
932 'name' => array(
933 'type' => 'string',
934 'sanitize' => 'sanitize_text_field',
935 ),
936 'id' => array( 'type' => 'integer' ),
937 )
938 );
939 $email = $data['email'] ?? '';
940 $name = $data['name'] ?? '';
941 $subscriber = array(
942 'email' => $email,
943 'name' => $name,
944 );
945 if ( isset( $data['id'] ) && is_int( $data['id'] ) && 0 < $data['id'] ) {
946 $subscriber['id'] = $data['id'];
947 }
948 $active_slugs = array_column( $this->service->get_modules(), 'slug' );
949 $modules = array_values(
950 array_filter(
951 $this->service->get_modules_as_objects(),
952 static function ( $module ) use ( $active_slugs ) {
953 return in_array( $module->slug, $active_slugs, true );
954 }
955 )
956 );
957 $sent = $this->service->send_invite_all_email( $subscriber, $modules );
958
959 return new Response( $sent, array() );
960 }
961 }
962