is_wp_org_version = defender_is_wp_org_version(); add_action( 'wp_ajax_defender_ip_detection_notice_dismiss', array( $this, 'dismiss_notice' ) ); add_action( 'wp_ajax_defender_ip_detection_switch_to_xff', array( $this, 'switch_to_xff' ) ); add_action( 'wp_ajax_defender_track_deactivate', array( $this, 'track_deactivate' ) ); add_action( 'admin_head', array( $this, 'add_global_styles' ) ); // Deactivation survey. add_action( 'admin_footer-plugins.php', array( $this, 'load_deactivation_survey_modal' ) ); } /** * Add global styles. */ public function add_global_styles() { echo ''; if ( ! $this->is_wp_org_version ) { echo ''; } } /** * Init admin actions. */ public function init() { // Display plugin links. add_filter( 'network_admin_plugin_action_links_' . DEFENDER_PLUGIN_BASENAME, array( $this, 'settings_link' ) ); add_filter( 'plugin_action_links_' . DEFENDER_PLUGIN_BASENAME, array( $this, 'settings_link' ) ); add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 3 ); // Only for plugin pages and actions are only for wp.org members. if ( $this->is_wp_org_version ) { /** * Action hook that fires after a scan issue is fixed. * * @since 4.4.0 */ add_action( 'wpdef_fixed_scan_issue', array( $this, 'after_scan_fix' ), 10 ); // For submenu callout. add_action( 'admin_head', array( $this, 'retarget_submenu_callout' ) ); if ( ! wd_di()->get( WPMUDEV::class )->is_wpmu_hosting() ) { $upsell_menu_title = sprintf( '%1$s%2$s', esc_html__( 'Upgrade', 'defender-security' ), esc_html__( 'Pro', 'defender-security' ) ); add_submenu_page( 'wp-defender', esc_html__( 'Upgrade to Pro', 'defender-security' ), $upsell_menu_title, is_multisite() ? 'manage_network_options' : 'manage_options', $this->get_link( 'upsell', 'defender_new-submenu_upsell' ) ); global $submenu; if ( isset( $submenu['wp-defender'] ) && is_array( $submenu['wp-defender'] ) && array() !== $submenu['wp-defender'] ) { $last = array_key_last( $submenu['wp-defender'] ); $submenu['wp-defender'][ $last ][4] = 'defender-menu-upsell'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited } } } // Display IP detection notice. if ( is_multisite() ) { add_action( 'network_admin_notices', array( $this, 'admin_notices' ) ); } else { add_action( 'admin_notices', array( $this, 'admin_notices' ) ); } } /** * Initialize the deactivation survey modal. */ public function init_deactivation_survey() { global $pagenow; if ( 'plugins.php' !== $pagenow ) { return; } wp_enqueue_style( 'def-sui' ); wp_enqueue_style( 'def-admin' ); if ( ! wp_script_is( 'clipboard', 'enqueued' ) ) { wp_enqueue_script( 'clipboard' ); } wp_enqueue_script( 'wpmudev-sui' ); wp_enqueue_script( 'def-deactivation-survey' ); wp_enqueue_script( 'def-admin' ); } /** * Retrieves the display name of the plugin. * * @return string The display name of the plugin with a trailing hyphen. */ public function get_plugin_display_name(): string { // Check if the plugin is the WordPress.org version (i.e., the free version) and set the label accordingly. $plugin_label = $this->is_wp_org_version ? esc_html__( 'Defender', 'defender-security' ) : esc_html__( 'Defender Pro', 'defender-security' ); // Instantiate the Dashboard_Whitelabel class only if necessary. $whitelabel = new Dashboard_Whitelabel(); // If whitelabeling is enabled and a custom name is provided, use it. if ( $whitelabel->can_whitelabel() ) { $custom_label = $whitelabel->get_plugin_name( Config_Hub_Helper::WDP_ID ); if ( is_string( $custom_label ) && '' !== trim( $custom_label ) ) { $plugin_label = $custom_label; } } // Return the final plugin label with the appended dash. return $plugin_label . ' - '; } /** * Generates the submenu callout for the WP Defender plugin. * * @return void */ public function retarget_submenu_callout(): void { ?> is_wp_org_version ? "{$wp_org}/support/plugin/defender-security/" : "{$domain}/get-support/"; break; case 'support_with_utm': $link = "{$domain}/hub2/support/{$utm_tags}"; break; case 'roadmap': $link = "{$domain}/roadmap/"; break; case 'pro_link': $link = "{$domain}/$adv_path"; break; default: $link = ''; break; } return $link; } /** * Adds a settings link on plugin page. * * @param array $links Current links. * * @return array */ public function settings_link( $links ) { $action_links = array(); $wpmu_dev = new WPMUDEV(); // Dashboard-link. $action_links['dashboard'] = '' . esc_html__( 'Dashboard', 'defender-security' ) . ''; // Documentation-link. $action_links['docs'] = '' . esc_html__( 'Docs', 'defender-security' ) . ''; if ( ! $wpmu_dev->is_member() ) { if ( WP_DEFENDER_PRO_PATH !== DEFENDER_PLUGIN_BASENAME ) { if ( ! wd_di()->get( WPMUDEV::class )->is_wpmu_hosting() ) { $action_links['upgrade'] = '' . esc_html__( 'Get Defender Pro', 'defender-security' ) . ''; } } elseif ( ! $wpmu_dev->is_hosted_site_connected_to_tfh() ) { $action_links['renew'] = '' . esc_html__( 'Renew Membership', 'defender-security' ) . ''; } } return array_merge( $action_links, $links ); } /** * Show row meta on the plugin screen. * * @param string[] $links Plugin Row Meta. * @param string $file Plugin Base file. * @param array $plugin_data Plugin data. * * @return array */ public function plugin_row_meta( $links, $file, $plugin_data ) { $row_meta = array(); if ( ! defined( 'DEFENDER_PLUGIN_BASENAME' ) || DEFENDER_PLUGIN_BASENAME !== $file ) { return $links; } // Change AuthorURI link. if ( isset( $links[1] ) ) { $author_uri = $this->is_wp_org_version ? 'https://profiles.wordpress.org/wpmudev/' : 'https://wpmudev.com/'; $author_uri = sprintf( '%s', $author_uri, esc_html__( 'WPMU DEV', 'defender-security' ) ); $links[1] = sprintf( /* translators: %s: Author URI. */ esc_html__( 'By %s', 'defender-security' ), $author_uri ); } if ( $this->is_wp_org_version ) { // Change AuthorURI link. if ( isset( $links[2] ) && false === strpos( $links[2], 'target="_blank"' ) ) { if ( ! isset( $plugin_data['slug'] ) && $plugin_data['Name'] ) { $links[2] = sprintf( '%s', esc_url( network_admin_url( 'plugin-install.php?tab=plugin-information&plugin=defender-security&TB_iframe=true&width=600&height=550' ) ), /* translators: %s: Plugin name. */ esc_attr( sprintf( esc_html__( 'More information about %s', 'defender-security' ), $plugin_data['Name'] ) ), esc_attr( $plugin_data['Name'] ), esc_html__( 'View details', 'defender-security' ) ); } else { $links[2] = str_replace( 'href=', 'target="_blank" href=', $links[2] ); } } $row_meta['rate'] = '' . Rate::get_rate_button_title() . ''; $row_meta['support'] = '' . esc_html__( 'Support', 'defender-security' ) . ''; } else { // Change 'Visit plugins' link to 'View details'. if ( isset( $links[2] ) && false !== strpos( $links[2], 'project/wp-defender' ) ) { $links[2] = sprintf( '%s', esc_url( $this->get_link( 'pro_link', '', 'project/wp-defender/' ) ), esc_html__( 'View details', 'defender-security' ) ); } $row_meta['support'] = '' . esc_html__( 'Premium Support', 'defender-security' ) . ''; } $row_meta['roadmap'] = '' . esc_html__( 'Roadmap', 'defender-security' ) . ''; return array_merge( $links, $row_meta ); } /** * Display IP detection notices: * - if user site is behind proxy, e.g. Cloudflare or something else, and only for admins, * - only on the plugin's pages. * * @return void */ public function admin_notices(): void { if ( ! current_user_can( 'manage_options' ) || ! is_defender_page() ) { return; } $header = $this->get_plugin_display_name(); if ( Firewall::is_cf_notice_ready() ) { $is_show = 'cf'; $class_notice = 'notice-info'; $header .= esc_html__( 'Cloudflare Usage Detected: Switched to CF-Connecting-IP for Better Compatibility', 'defender-security' ); } elseif ( Firewall::is_xff_notice_ready() ) { $is_show = 'xff'; $class_notice = 'notice-warning'; $header .= esc_html__( 'Improve IP Detection: We suggest Switching to X-Forward-For IP Detection Method', 'defender-security' ); } else { return; } ?>
esc_html__( 'Invalid request, you are not allowed to do that action.', 'defender-security' ) ) ); } $prop = defender_get_data_from_request( 'prop', 'p' ); $notice_type = '' !== trim( $prop ) ? $prop : false; if ( 'notice-for-cf' === $notice_type ) { update_site_option( Firewall::IP_DETECTION_CF_DISMISS_SLUG, true ); wp_send_json_success(); } elseif ( 'notice-for-xff' === $notice_type ) { update_site_option( Firewall::IP_DETECTION_XFF_DISMISS_SLUG, true ); wp_send_json_success(); } else { wp_send_json_error( array( 'message' => esc_html__( 'Invalid request, allowed data not provided.', 'defender-security' ) ) ); } } /** * Switch to XFF option. * * @return void */ public function switch_to_xff(): void { if ( ! current_user_can( 'manage_options' ) || ! check_ajax_referer( 'defender_ip_detection_notice_dismiss' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid request, you are not allowed to do that action.', 'defender-security' ) ) ); } // Change model's data. $model_firewall = wd_di()->get( Model\Setting\Firewall::class ); $model_firewall->http_ip_header = 'HTTP_X_FORWARDED_FOR'; $xff_ip = defender_get_data_from_request( 'HTTP_X_FORWARDED_FOR', 's' ); $xff_parts = preg_split( '/\s*,\s*/', $xff_ip ); $xff_parts = is_array( $xff_parts ) ? $xff_parts : array(); $xff_parts = array_map( 'trim', $xff_parts ); $xff_parts = array_filter( $xff_parts, static function ( $ip ) { return false !== filter_var( $ip, FILTER_VALIDATE_IP ); } ); $separator = "\r\n"; $xff_parts = array_unique( $xff_parts ); $xff_ip = implode( $separator, $xff_parts ); if ( '' === $xff_ip ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid trusted proxy IP(s) detected in X-Forwarded-For header.', 'defender-security' ) ) ); } if ( '' === $model_firewall->trusted_proxies_ip ) { $model_firewall->trusted_proxies_ip = $xff_ip; } else { // Todo: improve the code using a separate method. This will be useful when the user switches between different proxy headeres (IP detection options). $model_firewall->trusted_proxies_ip = $model_firewall->trusted_proxies_ip . $separator . $xff_ip; } $model_firewall->save(); // Save Dismiss slug. update_site_option( Firewall::IP_DETECTION_XFF_DISMISS_SLUG, true ); wp_send_json_success(); } /** * Load deactivation survey modal. */ public function load_deactivation_survey_modal() { $deactivation_survey_template_file = WP_DEFENDER_DIR . 'src' . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR . 'modal' . DIRECTORY_SEPARATOR . 'deactivation-survey.php'; if ( ! file_exists( $deactivation_survey_template_file ) ) { return; } // Data to be passed to the template file. $is_pro = wd_di()->get( WPMUDEV::class )->is_pro(); $docs_link = $this->get_link( 'support_with_utm', 'defender_deactivation_survey_help', '', $is_pro ? 'defender-pro' : 'defender' ); ob_start(); require_once $deactivation_survey_template_file; // Everything escaped in all template files. echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** * Track deactivation. */ public function track_deactivate() { if ( ! current_user_can( 'manage_options' ) || ! check_ajax_referer( 'defender_deactivation_survey_modal' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid request, you are not allowed to do that action.', 'defender-security' ) ) ); } $posted_data = defender_get_data_from_request( null, 'p' ); if ( ! is_array( $posted_data['properties'] ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid request, allowed data not provided.', 'defender-security' ) ) ); } $properties = $posted_data['properties']; if ( ! isset( $properties['Reason'], $properties['Message'], $properties['Modal Action'], $properties['Requested Assistance'], $properties['Tracking Status'] ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Missing field(s).', 'defender-security' ) ) ); } wd_di()->get( Deactivation_Survey::class )->track_deactivation_survey( $properties ); wp_send_json_success(); } }