PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.8.0
Defender Security – Malware Scanner, Login Security & Firewall v5.8.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 / class-admin.php

class-admin.php in Defender Security – Malware Scanner, Login Security & Firewall 5.8.0, at src/class-admin.php

625 lines 20.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles WordPress admin page related tasks.
4 *
5 * @package WP_Defender
6 */
7
8 namespace WP_Defender;
9
10 use WP_Defender\Component\Rate;
11 use WP_Defender\Behavior\WPMUDEV;
12 use WP_Defender\Component\Firewall;
13 use WP_Defender\Integrations\Dashboard_Whitelabel;
14 use WP_Defender\Component\Config\Config_Hub_Helper;
15 use WP_Defender\Helper\Analytics\Deactivation_Survey;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 die();
19 }
20
21 /**
22 * Handles WordPress admin page related tasks.
23 *
24 * @since 2.4
25 */
26 class Admin {
27
28 /**
29 * Is the Free version?
30 *
31 * @var bool
32 */
33 public $is_wp_org_version;
34
35 /**
36 * Constructor for the Admin class.
37 */
38 public function __construct() {
39 $this->is_wp_org_version = defender_is_wp_org_version();
40 add_action( 'wp_ajax_defender_ip_detection_notice_dismiss', array( $this, 'dismiss_notice' ) );
41 add_action( 'wp_ajax_defender_ip_detection_switch_to_xff', array( $this, 'switch_to_xff' ) );
42 add_action( 'wp_ajax_defender_track_deactivate', array( $this, 'track_deactivate' ) );
43 add_action( 'admin_head', array( $this, 'add_global_styles' ) );
44
45 // Deactivation survey.
46 add_action( 'admin_footer-plugins.php', array( $this, 'load_deactivation_survey_modal' ) );
47 }
48
49 /**
50 * Add global styles.
51 */
52 public function add_global_styles() {
53 echo '<style>
54 #toplevel_page_wp-defender ul.wp-submenu li a[href="admin.php?page=wdf-ip-lockout"] { display: flex; justify-content: space-between; align-items: center; }
55 </style>';
56 }
57
58 /**
59 * Init admin actions.
60 */
61 public function init() {
62 // Display plugin links.
63 add_filter( 'network_admin_plugin_action_links_' . DEFENDER_PLUGIN_BASENAME, array( $this, 'settings_link' ) );
64 add_filter( 'plugin_action_links_' . DEFENDER_PLUGIN_BASENAME, array( $this, 'settings_link' ) );
65 add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 3 );
66 // Only for plugin pages and actions are only for wp.org members.
67 if ( $this->is_wp_org_version ) {
68 /**
69 * Action hook that fires after a scan issue is fixed.
70 *
71 * @since 4.4.0
72 */
73 add_action(
74 'wpdef_fixed_scan_issue',
75 array( $this, 'after_scan_fix' ),
76 10
77 );
78 // For submenu callout.
79 add_action( 'admin_head', array( $this, 'retarget_submenu_callout' ) );
80 if ( ! wd_di()->get( WPMUDEV::class )->is_wpmu_hosting() ) {
81 add_submenu_page(
82 'wp-defender',
83 esc_html__( 'Get Defender Pro', 'defender-security' ),
84 esc_html__( 'Get Defender Pro', 'defender-security' ),
85 is_multisite() ? 'manage_network_options' : 'manage_options',
86 $this->get_link( 'upsell', 'defender_submenu_upsell' )
87 );
88 }
89 }
90
91 // Display IP detection notice.
92 if ( is_multisite() ) {
93 add_action( 'network_admin_notices', array( $this, 'admin_notices' ) );
94 } else {
95 add_action( 'admin_notices', array( $this, 'admin_notices' ) );
96 }
97 }
98
99 /**
100 * Initialize the deactivation survey modal.
101 */
102 public function init_deactivation_survey() {
103 global $pagenow;
104
105 if ( 'plugins.php' !== $pagenow ) {
106 return;
107 }
108
109 wp_enqueue_style( 'def-sui' );
110 wp_enqueue_style( 'def-admin' );
111
112 if ( ! wp_script_is( 'clipboard', 'enqueued' ) ) {
113 wp_enqueue_script( 'clipboard' );
114 }
115
116 wp_enqueue_script( 'wpmudev-sui' );
117 wp_enqueue_script( 'def-deactivation-survey' );
118 wp_enqueue_script( 'def-admin' );
119 }
120
121 /**
122 * Retrieves the display name of the plugin.
123 *
124 * @return string The display name of the plugin with a trailing hyphen.
125 */
126 public function get_plugin_display_name(): string {
127 // Check if the plugin is the WordPress.org version (i.e., the free version) and set the label accordingly.
128 $plugin_label = $this->is_wp_org_version
129 ? esc_html__( 'Defender', 'defender-security' )
130 : esc_html__( 'Defender Pro', 'defender-security' );
131
132 // Instantiate the Dashboard_Whitelabel class only if necessary.
133 $whitelabel = new Dashboard_Whitelabel();
134
135 // If whitelabeling is enabled and a custom name is provided, use it.
136 if ( $whitelabel->can_whitelabel() ) {
137 $custom_label = $whitelabel->get_plugin_name( Config_Hub_Helper::WDP_ID );
138 if ( is_string( $custom_label ) && '' !== trim( $custom_label ) ) {
139 $plugin_label = $custom_label;
140 }
141 }
142
143 // Return the final plugin label with the appended dash.
144 return $plugin_label . ' - ';
145 }
146
147 /**
148 * The method is a stub without content.
149 */
150 private function menu_nope(): void {
151 }
152
153 /**
154 * Generates the submenu callout for the WP Defender plugin.
155 *
156 * @return void
157 */
158 public function retarget_submenu_callout(): void {
159 ?>
160 <style>
161 #toplevel_page_wp-defender > ul > li:last-child > a[href^="https://wpmudev.com/"],
162 #toplevel_page_wp-defender > ul > li:last-child > a[href^="https://wpmudev.com/"]:hover,
163 #toplevel_page_wp-defender > ul > li:last-child > a[href^="https://wpmudev.com/"]:active,
164 #toplevel_page_wp-defender > ul > li:last-child > a[href^="https://wpmudev.com/"]:focus {
165 background: #8D00B1;
166 color: #ffffff;
167 font-weight: 500;
168 }
169
170 #toplevel_page_wp-defender.wp-not-current-submenu > ul > li:last-child > a[href^="https://wpmudev.com/"],
171 #toplevel_page_wp-defender.wp-not-current-submenu > ul > li:last-child > a[href^="https://wpmudev.com/"]:hover,
172 #toplevel_page_wp-defender.wp-not-current-submenu > ul > li:last-child > a[href^="https://wpmudev.com/"]:active,
173 #toplevel_page_wp-defender.wp-not-current-submenu > ul > li:last-child > a[href^="https://wpmudev.com/"]:focus {
174 margin-left: -4px;
175 }
176 </style>
177 <script type='text/javascript'>
178 jQuery(function ($) {
179 $('#toplevel_page_wp-defender > ul > li:last-child > a[href^="https://wpmudev.com/"]').attr("target", "_blank");
180 });
181 </script>
182 <?php
183 }
184
185 /**
186 * Fired when the scan issue is fixed.
187 *
188 * @return void
189 */
190 public function after_scan_fix(): void {
191 Rate::run_counter_of_fixed_scans();
192 }
193
194 /**
195 * Return URL link.
196 *
197 * @param string $link_for Accepts: 'docs', 'plugin', 'rate' and etc.
198 * @param string $campaign Utm campaign tag to be used in link. Default: ''.
199 * @param string $adv_path Advanced path. Default: ''.
200 * @param string $source UTM source. Default: 'defender'.
201 *
202 * @return string
203 */
204 public function get_link( $link_for, $campaign = '', $adv_path = '', $source = 'defender' ): string {
205 $domain = 'https://wpmudev.com';
206 $wp_org = 'https://wordpress.org';
207 $utm_tags = "?utm_source={$source}&utm_medium=plugin&utm_campaign={$campaign}";
208 switch ( $link_for ) {
209 case 'docs':
210 $link = "{$domain}/docs/wpmu-dev-plugins/defender/{$utm_tags}";
211 break;
212 case 'plugin':
213 case 'upsell':
214 $link = "{$domain}/project/wp-defender/{$utm_tags}";
215 break;
216 case 'rate':
217 $link = "{$wp_org}/support/plugin/defender-security/reviews/#new-post";
218 break;
219 case 'support':
220 $link = $this->is_wp_org_version
221 ? "{$wp_org}/support/plugin/defender-security/"
222 : "{$domain}/get-support/";
223 break;
224 case 'support_with_utm':
225 $link = "{$domain}/hub2/support/{$utm_tags}";
226 break;
227 case 'roadmap':
228 $link = "{$domain}/roadmap/";
229 break;
230 case 'pro_link':
231 $link = "{$domain}/$adv_path";
232 break;
233 default:
234 $link = '';
235 break;
236 }
237
238 return $link;
239 }
240
241 /**
242 * Adds a settings link on plugin page.
243 *
244 * @param array $links Current links.
245 *
246 * @return array
247 */
248 public function settings_link( $links ) {
249 $action_links = array();
250 $wpmu_dev = new WPMUDEV();
251 // Dashboard-link.
252 $action_links['dashboard'] = '<a href="' . network_admin_url( 'admin.php?page=wp-defender' ) . '" aria-label="' . esc_attr(
253 esc_html__(
254 'Go to Defender Dashboard',
255 'defender-security'
256 )
257 ) . '">' . esc_html__( 'Dashboard', 'defender-security' ) . '</a>';
258 // Documentation-link.
259 $action_links['docs'] = '<a target="_blank" href="' . $this->get_link(
260 'docs',
261 'defender_pluginlist_docs'
262 ) . '" aria-label="' . esc_attr(
263 esc_html__(
264 'Docs',
265 'defender-security'
266 )
267 ) . '">' . esc_html__( 'Docs', 'defender-security' ) . '</a>';
268 if ( ! $wpmu_dev->is_member() ) {
269 if ( WP_DEFENDER_PRO_PATH !== DEFENDER_PLUGIN_BASENAME ) {
270 if ( ! wd_di()->get( WPMUDEV::class )->is_wpmu_hosting() ) {
271 $action_links['upgrade'] = '<a style="color: #8D00B1;" target="_blank" href="' . $this->get_link(
272 'plugin',
273 'defender_pluginlist_upgrade'
274 ) . '" aria-label="' . esc_attr(
275 esc_html__(
276 'Upgrade to Defender Pro',
277 'defender-security'
278 )
279 ) . '">' . esc_html__( 'Get Defender Pro', 'defender-security' ) . '</a>';
280 }
281 } elseif ( ! $wpmu_dev->is_hosted_site_connected_to_tfh() ) {
282 $action_links['renew'] = '<a style="color: #8D00B1;" target="_blank" href="' . $this->get_link(
283 'plugin',
284 'defender_pluginlist_renew'
285 ) . '" aria-label="' . esc_attr(
286 esc_html__(
287 'Renew Your Membership',
288 'defender-security'
289 )
290 ) . '">' . esc_html__( 'Renew Membership', 'defender-security' ) . '</a>';
291 }
292 }
293
294 return array_merge( $action_links, $links );
295 }
296
297 /**
298 * Show row meta on the plugin screen.
299 *
300 * @param string[] $links Plugin Row Meta.
301 * @param string $file Plugin Base file.
302 * @param array $plugin_data Plugin data.
303 *
304 * @return array
305 */
306 public function plugin_row_meta( $links, $file, $plugin_data ) {
307 $row_meta = array();
308 if ( ! defined( 'DEFENDER_PLUGIN_BASENAME' ) || DEFENDER_PLUGIN_BASENAME !== $file ) {
309 return $links;
310 }
311
312 // Change AuthorURI link.
313 if ( isset( $links[1] ) ) {
314 $author_uri = $this->is_wp_org_version ? 'https://profiles.wordpress.org/wpmudev/' : 'https://wpmudev.com/';
315 $author_uri = sprintf(
316 '<a href="%s" target="_blank">%s</a>',
317 $author_uri,
318 esc_html__( 'WPMU DEV', 'defender-security' )
319 );
320 $links[1] = sprintf(
321 /* translators: %s: Author URI. */
322 esc_html__( 'By %s', 'defender-security' ),
323 $author_uri
324 );
325 }
326
327 if ( $this->is_wp_org_version ) {
328 // Change AuthorURI link.
329 if ( isset( $links[2] ) && false === strpos( $links[2], 'target="_blank"' ) ) {
330 if ( ! isset( $plugin_data['slug'] ) && $plugin_data['Name'] ) {
331 $links[2] = sprintf(
332 '<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>',
333 esc_url(
334 network_admin_url(
335 'plugin-install.php?tab=plugin-information&plugin=defender-security&TB_iframe=true&width=600&height=550'
336 )
337 ),
338 /* translators: %s: Plugin name. */
339 esc_attr( sprintf( esc_html__( 'More information about %s', 'defender-security' ), $plugin_data['Name'] ) ),
340 esc_attr( $plugin_data['Name'] ),
341 esc_html__( 'View details', 'defender-security' )
342 );
343 } else {
344 $links[2] = str_replace( 'href=', 'target="_blank" href=', $links[2] );
345 }
346 }
347 $row_meta['rate'] = '<a href="' . esc_url( $this->get_link( 'rate' ) ) . '" aria-label="' . esc_attr__(
348 'Rate Defender',
349 'defender-security'
350 ) . '" target="_blank">' . Rate::get_rate_button_title() . '</a>';
351 $row_meta['support'] = '<a href="' . esc_url( $this->get_link( 'support' ) ) . '" aria-label="' . esc_attr__(
352 'Support',
353 'defender-security'
354 ) . '" target="_blank">' . esc_html__( 'Support', 'defender-security' ) . '</a>';
355 } else {
356 // Change 'Visit plugins' link to 'View details'.
357 if ( isset( $links[2] ) && false !== strpos( $links[2], 'project/wp-defender' ) ) {
358 $links[2] = sprintf(
359 '<a href="%s" target="_blank">%s</a>',
360 esc_url( $this->get_link( 'pro_link', '', 'project/wp-defender/' ) ),
361 esc_html__( 'View details', 'defender-security' )
362 );
363 }
364 $row_meta['support'] = '<a href="' . esc_url( $this->get_link( 'support' ) ) . '" aria-label="' . esc_attr__(
365 'Premium Support',
366 'defender-security'
367 ) . '" target="_blank">' . esc_html__( 'Premium Support', 'defender-security' ) . '</a>';
368 }
369 $row_meta['roadmap'] = '<a href="' . esc_url( $this->get_link( 'roadmap' ) ) . '" aria-label="' . esc_attr__(
370 'Roadmap',
371 'defender-security'
372 ) . '" target="_blank">' . esc_html__( 'Roadmap', 'defender-security' ) . '</a>';
373
374 return array_merge( $links, $row_meta );
375 }
376
377 /**
378 * Display IP detection notices:
379 * - if user site is behind proxy, e.g. Cloudflare or something else, and only for admins,
380 * - only on the plugin's pages.
381 *
382 * @return void
383 */
384 public function admin_notices(): void {
385 if ( ! current_user_can( 'manage_options' ) || ! is_defender_page() ) {
386 return;
387 }
388 $header = $this->get_plugin_display_name();
389 if ( Firewall::is_cf_notice_ready() ) {
390 $is_show = 'cf';
391 $class_notice = 'notice-info';
392 $header .= esc_html__(
393 'Cloudflare Usage Detected: Switched to CF-Connecting-IP for Better Compatibility',
394 'defender-security'
395 );
396 } elseif ( Firewall::is_xff_notice_ready() ) {
397 $is_show = 'xff';
398 $class_notice = 'notice-warning';
399 $header .= esc_html__(
400 'Improve IP Detection: We suggest Switching to X-Forward-For IP Detection Method',
401 'defender-security'
402 );
403 } else {
404 return;
405 }
406 ?>
407 <div class="defender_ip_detection_notice notice <?php echo esc_attr( $class_notice ); ?> is-dismissible"
408 data-nonce="<?php echo esc_attr( wp_create_nonce( 'defender_ip_detection_notice_dismiss' ) ); ?>"
409 data-prop="notice-for-<?php echo esc_attr( $is_show ); ?>">
410 <h3 style="margin-bottom:0;">
411 <?php echo esc_html( $header ); ?>
412 </h3>
413 <?php if ( 'cf' === $is_show ) { ?>
414 <p style="color: #72777C; line-height: 22px;">
415 <?php
416 printf(
417 /* translators: %s: Link. */
418 esc_html__(
419 'We have switched to using the CF-Connecting-IP HTTP header for IP detection, offering enhanced compatibility for users behind Cloudflare Proxy. If you wish to change this setting, you can do so from %s.',
420 'defender-security'
421 ),
422 '<a style="font-weight:bold;" href="' . esc_url_raw( network_admin_url( 'admin.php?page=wdf-ip-lockout&view=settings#detect-ip-addresses' ) ) . '">' . esc_html__( 'here', 'defender-security' ) . '</a>'
423 );
424 ?>
425 </p>
426 <p>
427 <button type="button" class="button button-primary button-large defender_ip_detection_action_hide"
428 data-prop="defender_ip_detection_notice_success">
429 <?php
430 esc_html_e(
431 'Ok, I understand',
432 'defender-security'
433 );
434 ?>
435 </button>
436 </p>
437 <?php } elseif ( 'xff' === $is_show ) { ?>
438 <p style="color: #72777C; line-height: 22px;">
439 <?php
440 printf(
441 /* translators: %s: Link. */
442 esc_html__(
443 'Based on your server configuration, we recommend switching to the X-Forwarded-For method for accurate IP detection and to prevent firewall blocks. Easily modify your settings %s.',
444 'defender-security'
445 ),
446 '<a style="font-weight:bold;" href="' . esc_url_raw( network_admin_url( 'admin.php?page=wdf-ip-lockout&view=settings#detect-ip-addresses' ) ) . '">' . esc_html__( 'here', 'defender-security' ) . '</a>'
447 );
448 ?>
449 </p>
450 <p>
451 <button type="button" class="button button-primary button-large"
452 id="defender_ip_detection_action_switch"
453 data-prop="defender_ip_detection_notice_success">
454 <?php esc_html_e( 'Switch to X-Forwarded-For', 'defender-security' ); ?>
455 </button>
456 <a href="#" class="defender_ip_detection_action_hide"
457 style="margin-left: 11px; line-height: 16px; text-decoration: none; font-weight: bold;"
458 data-prop="defender_ip_detection_notice_dismiss"><?php esc_html_e( 'Dismiss', 'defender-security' ); ?></a>
459 </p>
460 <?php } ?>
461 </div>
462 <script type="text/javascript">
463 //Switch.
464 jQuery('#defender_ip_detection_action_switch').on('click', function (e) {
465 e.preventDefault();
466 var $notice = jQuery(e.currentTarget).closest('.defender_ip_detection_notice'),
467 ajaxUrl = '<?php echo esc_url_raw( admin_url( 'admin-ajax.php' ) ); ?>';
468
469 jQuery.post(
470 ajaxUrl,
471 {
472 action: 'defender_ip_detection_switch_to_xff',
473 _ajax_nonce: $notice.data('nonce')
474 }
475 ).always(function () {
476 $notice.hide();
477 });
478 });
479 //Hide.
480 jQuery('body').on('click', '.defender_ip_detection_notice .notice-dismiss, .defender_ip_detection_action_hide', function (e) {
481 e.preventDefault();
482 var $notice = jQuery(e.currentTarget).closest('.defender_ip_detection_notice'),
483 ajaxUrl = '<?php echo esc_url_raw( admin_url( 'admin-ajax.php' ) ); ?>';
484
485 jQuery.post(
486 ajaxUrl,
487 {
488 action: 'defender_ip_detection_notice_dismiss',
489 prop: $notice.data('prop'),
490 _ajax_nonce: $notice.data('nonce')
491 }
492 ).always(function () {
493 $notice.hide();
494 });
495 });
496 </script>
497 <?php
498 }
499
500 /**
501 * Dismiss notice.
502 *
503 * @return void
504 */
505 public function dismiss_notice(): void {
506 if (
507 ! current_user_can( 'manage_options' ) ||
508 ! check_ajax_referer( 'defender_ip_detection_notice_dismiss' )
509 ) {
510 wp_send_json_error(
511 array( 'message' => esc_html__( 'Invalid request, you are not allowed to do that action.', 'defender-security' ) )
512 );
513 }
514
515 $prop = defender_get_data_from_request( 'prop', 'p' );
516 $notice_type = '' !== trim( $prop ) ? $prop : false;
517 if ( 'notice-for-cf' === $notice_type ) {
518 update_site_option( Firewall::IP_DETECTION_CF_DISMISS_SLUG, true );
519 wp_send_json_success();
520 } elseif ( 'notice-for-xff' === $notice_type ) {
521 update_site_option( Firewall::IP_DETECTION_XFF_DISMISS_SLUG, true );
522 wp_send_json_success();
523 } else {
524 wp_send_json_error(
525 array( 'message' => esc_html__( 'Invalid request, allowed data not provided.', 'defender-security' ) )
526 );
527 }
528 }
529
530 /**
531 * Switch to XFF option.
532 *
533 * @return void
534 */
535 public function switch_to_xff(): void {
536 if (
537 ! current_user_can( 'manage_options' ) ||
538 ! check_ajax_referer( 'defender_ip_detection_notice_dismiss' )
539 ) {
540 wp_send_json_error(
541 array( 'message' => esc_html__( 'Invalid request, you are not allowed to do that action.', 'defender-security' ) )
542 );
543 }
544 // Change model's data.
545 $model_firewall = wd_di()->get( Model\Setting\Firewall::class );
546 $model_firewall->http_ip_header = 'HTTP_X_FORWARDED_FOR';
547 $xff_ip = defender_get_data_from_request( 'HTTP_X_FORWARDED_FOR', 's' );
548 if ( '' === $model_firewall->trusted_proxies_ip ) {
549 $model_firewall->trusted_proxies_ip = $xff_ip;
550 } else {
551 // Todo: improve the code using a separate method. This will be useful when the user switches between different proxy headeres (IP detection options).
552 $separator = "\r\n";
553 // Check if the XFF header contains multiple IPs.
554 $xff_ip = str_replace( array( ',', ' ,' ), $separator, $xff_ip );
555 $model_firewall->trusted_proxies_ip = $model_firewall->trusted_proxies_ip . $separator . $xff_ip;
556 }
557 $model_firewall->save();
558 // Save Dismiss slug.
559 update_site_option( Firewall::IP_DETECTION_XFF_DISMISS_SLUG, true );
560 wp_send_json_success();
561 }
562
563 /**
564 * Load deactivation survey modal.
565 */
566 public function load_deactivation_survey_modal() {
567 $deactivation_survey_template_file = WP_DEFENDER_DIR .
568 'src' . DIRECTORY_SEPARATOR .
569 'view' . DIRECTORY_SEPARATOR .
570 'modal' . DIRECTORY_SEPARATOR .
571 'deactivation-survey.php';
572
573 if ( ! file_exists( $deactivation_survey_template_file ) ) {
574 return;
575 }
576
577 // Data to be passed to the template file.
578 $is_pro = wd_di()->get( WPMUDEV::class )->is_pro();
579 $docs_link = $this->get_link( 'support_with_utm', 'defender_deactivation_survey_help', '', $is_pro ? 'defender-pro' : 'defender' );
580
581 ob_start();
582 require_once $deactivation_survey_template_file;
583 // Everything escaped in all template files.
584 echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
585 }
586
587 /**
588 * Track deactivation.
589 */
590 public function track_deactivate() {
591 if (
592 ! current_user_can( 'manage_options' ) ||
593 ! check_ajax_referer( 'defender_deactivation_survey_modal' )
594 ) {
595 wp_send_json_error(
596 array( 'message' => esc_html__( 'Invalid request, you are not allowed to do that action.', 'defender-security' ) )
597 );
598 }
599 $posted_data = defender_get_data_from_request( null, 'p' );
600 if ( ! is_array( $posted_data['properties'] ) ) {
601 wp_send_json_error(
602 array( 'message' => esc_html__( 'Invalid request, allowed data not provided.', 'defender-security' ) )
603 );
604 }
605 $properties = $posted_data['properties'];
606 if (
607 ! isset(
608 $properties['Reason'],
609 $properties['Message'],
610 $properties['Modal Action'],
611 $properties['Requested Assistance'],
612 $properties['Tracking Status']
613 )
614 ) {
615 wp_send_json_error(
616 array( 'message' => esc_html__( 'Missing field(s).', 'defender-security' ) )
617 );
618 }
619
620 wd_di()->get( Deactivation_Survey::class )->track_deactivation_survey( $properties );
621
622 wp_send_json_success();
623 }
624 }
625