settings = $settings; $this->mailer = $mailer; add_action( 'admin_menu', array( $this, 'register_menu' ) ); add_action( 'admin_post_wpcev_save_settings', array( $this, 'handle_save' ) ); add_action( 'admin_post_wpcev_send_test_email', array( $this, 'handle_test_email' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); add_filter( 'plugin_action_links_' . WPCEV_PLUGIN_BASENAME, array( $this, 'plugin_action_links' ) ); } public function register_menu() { add_options_page( __( 'Email Verify', 'wpcoretools-email-verify' ), __( 'Email Verify', 'wpcoretools-email-verify' ), 'manage_options', self::PAGE_SLUG, array( $this, 'render_page' ) ); } public function plugin_action_links( $links ) { $url = admin_url( 'options-general.php?page=' . self::PAGE_SLUG ); array_unshift( $links, '' . esc_html__( 'Settings', 'wpcoretools-email-verify' ) . '' ); return $links; } public function enqueue_assets( $hook ) { $is_settings = ( 'settings_page_' . self::PAGE_SLUG === $hook ); $is_wizard = ( WPCEV_Wizard::SCREEN_ID === $hook ); $is_verifications = ( 'users_page_' . WPCEV_Verifications_Page::PAGE_SLUG === $hook ); // WP main dashboard: only load CSS for users who can see our widget. $is_dashboard = ( 'index.php' === $hook && current_user_can( 'manage_options' ) ); // Users list: the status badges of the Verified column. $is_users = ( 'users.php' === $hook && current_user_can( 'list_users' ) ); if ( ! $is_settings && ! $is_wizard && ! $is_dashboard && ! $is_verifications && ! $is_users ) { return; } // Use file mtime as part of the cache key so CSS edits between // releases bust the browser cache reliably (otherwise WPCEV_VERSION // stays "1.0.0" through every rebuild and admins keep seeing a // stale stylesheet). $css_path = WPCEV_PLUGIN_DIR . 'assets/css/admin.css'; $ver = is_readable( $css_path ) ? WPCEV_VERSION . '.' . filemtime( $css_path ) : WPCEV_VERSION; wp_enqueue_style( 'wpcev-admin', WPCEV_PLUGIN_URL . 'assets/css/admin.css', array(), $ver ); } public function handle_save() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'wpcoretools-email-verify' ) ); } check_admin_referer( 'wpcev_save_settings' ); // $_POST['wpcev'] is a structured array; per-field sanitization happens in // WPCEV_Settings::sanitize(), called by ->save(). Sanitizing the raw array // here would corrupt nested values before that schema-aware step. // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $raw = isset( $_POST['wpcev'] ) && is_array( $_POST['wpcev'] ) ? wp_unslash( $_POST['wpcev'] ) : array(); $clean = $this->settings->save( $raw, true ); // Auto-delete just switched on (or its event went missing): make sure the // daily event exists. The handler itself does nothing while the setting is off. if ( ! empty( $clean['auto_delete_unverified'] ) ) { WPCEV_Purge::schedule(); } wp_safe_redirect( add_query_arg( array( 'page' => self::PAGE_SLUG, 'updated' => 1, ), admin_url( 'options-general.php' ) ) ); exit; } public function handle_test_email() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'wpcoretools-email-verify' ) ); } check_admin_referer( 'wpcev_send_test_email' ); $user = wp_get_current_user(); $res = $this->mailer->send_test( $user->ID, $user->user_email ); wp_safe_redirect( add_query_arg( array( 'page' => self::PAGE_SLUG, 'wpcev_test' => $res, ), admin_url( 'options-general.php' ) ) ); exit; } public function render_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only flags from our own redirects. $saved = ! empty( $_GET['updated'] ); $wizard_ok = ! empty( $_GET['wpcev_done'] ); $test_state = isset( $_GET['wpcev_test'] ) ? sanitize_key( wp_unslash( $_GET['wpcev_test'] ) ) : ''; $current_tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'dashboard'; // phpcs:enable WordPress.Security.NonceVerification.Recommended $valid_tabs = array( 'dashboard', 'settings', 'tools', 'fail-log' ); if ( ! in_array( $current_tab, $valid_tabs, true ) ) { $current_tab = 'dashboard'; } ?>

user_email ) ); ?>

render_tabs( $current_tab ); ?> render_tab_fail_log(); break; case 'tools': $this->render_tab_tools(); break; case 'settings': $this->render_tab_settings(); break; default: $this->render_tab_dashboard(); } ?>
__( 'Dashboard', 'wpcoretools-email-verify' ), 'settings' => __( 'Settings', 'wpcoretools-email-verify' ), 'tools' => __( 'Tools', 'wpcoretools-email-verify' ), 'fail-log' => __( 'Failed sends', 'wpcoretools-email-verify' ), ); echo ''; } /** * Default landing tab — at-a-glance status of verification across the * site, with the most-recent pending users and quick links to the * surfaces an admin actually wants from here. */ private function render_tab_dashboard() { $counts = WPCEV_Stats::counts(); $recent = WPCEV_Stats::recent_unverified( 5 ); $failures = WPCEV_Stats::get_failures(); $verifications_url = class_exists( 'WPCEV_Verifications_Page' ) ? admin_url( 'users.php?page=' . WPCEV_Verifications_Page::PAGE_SLUG ) : ''; ?>
render_reconfigure_callout(); ?> render_companion_callout(); ?>

settings->all(); $wizard_url = function_exists( 'menu_page_url' ) ? menu_page_url( WPCEV_Wizard::PAGE_SLUG, false ) : admin_url( 'admin.php?page=' . WPCEV_Wizard::PAGE_SLUG ); $wc_active = class_exists( 'WooCommerce' ); $profile = WPCEV_Presets::match( $s ); $cards = WPCEV_Presets::cards( $wc_active ); ?>
settings->all(); $roles = wp_roles()->roles; ?> render_reconfigure_callout(); ?>

render_companion_callout(); ?>
'; } ?>


' . esc_html__( 'Failure log cleared.', 'wpcoretools-email-verify' ) . '

'; } $failures = WPCEV_Stats::get_failures(); ?>

user_login ); ?> #

purge ? wpcev()->purge : new WPCEV_Purge( $this->settings, wpcev()->tokens ); $wizard_url = function_exists( 'menu_page_url' ) ? menu_page_url( WPCEV_Wizard::PAGE_SLUG, false ) : admin_url( 'admin.php?page=' . WPCEV_Wizard::PAGE_SLUG ); // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only flags from our own redirects. $scanned = ! empty( $_GET['wpcev_scanned'] ); $purged_count = isset( $_GET['wpcev_purged'] ) ? (int) $_GET['wpcev_purged'] : -1; $unverified_all_count = isset( $_GET['wpcev_unverified_all'] ) ? (int) $_GET['wpcev_unverified_all'] : -1; // phpcs:enable WordPress.Security.NonceVerification.Recommended $auto_delete_on = (bool) $this->settings->get( 'auto_delete_unverified' ); $threshold_days = (int) $this->settings->get( 'auto_delete_after_days', 30 ); $next_cron = wp_next_scheduled( WPCEV_Purge::CRON_HOOK ); $eligible = $purge->eligible_count(); $grandfathered = $purge->grandfathered_count(); ?>

= 0 ) : ?>

= 0 ) : ?>

render_safety_check(); ?>

wc_backfill ) { wpcev()->wc_backfill->render_panel(); } ?>

conflicts; $issues = $conflicts ? $conflicts->detect() : array(); $shown = 0; echo ''; if ( 0 === $shown ) { echo '
' . esc_html__( 'All clear.', 'wpcoretools-email-verify' ) . ' ' . esc_html__( 'No other email verification, risky WooCommerce setting or mail problem was found.', 'wpcoretools-email-verify' ) . '
'; } if ( $conflicts ) { $conflicts->render_scan_panel( 'tools' ); } } /** * A short note when the sibling Disposable Email Guard plugin is active: * burner addresses are rejected before this plugin sends its mail. It only * reports what runs on the site; when the sibling is absent nothing is shown. */ private function render_companion_callout() { if ( ! defined( 'WPCDEG_VERSION' ) ) { return; } ?>