logger = $logger; $this->get_logger()->debug( 'Initializing AJAX handlers', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, ] ); add_action( 'wp_ajax_f12_doi_details', [ $this, 'getDetails' ] ); add_action( 'wp_ajax_f12_doi_templateloader', [ $this, 'getTemplate' ] ); $this->get_logger()->info( 'AJAX handlers registered', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, ] ); } public function get_logger() { return $this->logger; } /** * Load and get the template we need. */ public function getTemplate() { // A nonce proves intent, not authorization — gate on capability too. if ( ! current_user_can( 'manage_options' ) ) { wp_die( -1, 403 ); } $this->get_logger()->debug( 'getTemplate called', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'post' => $_POST, ] ); $content = ''; if ( isset( $_POST['template'] ) && wp_verify_nonce( wp_unslash( $_POST['nonce'] ), 'f12_doi_templateloader' ) ) { // Use sanitize_file_name() to prevent path traversal attacks $template_name = sanitize_file_name( wp_unslash( $_POST['template'] ) ); $mails_dir = plugin_dir_path( dirname( __FILE__ ) ) . 'mails/'; $template_path = $mails_dir . $template_name . '.html'; // Verify the resolved path is within the allowed directory (prevent path traversal) $real_template_path = realpath( $template_path ); $real_mails_dir = realpath( $mails_dir ); if ( $real_template_path && $real_mails_dir && strpos( $real_template_path, $real_mails_dir ) === 0 && file_exists( $real_template_path ) ) { $this->get_logger()->debug( 'Template file found, loading content', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'template_path' => $template_path, ] ); $content = file_get_contents( $real_template_path ); } else { $this->get_logger()->warning( 'Template file not found', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'template_path' => $template_path, ] ); } } else { $this->get_logger()->warning( 'Invalid or missing nonce/template parameter', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'post' => $_POST, ] ); } echo wp_json_encode( [ 'status' => 200, 'content' => $content ] ); wp_die(); } /** * Return the Popup for the HASH DOI */ public function getDetails() { // Exposes opt-in PII (email, IPs, all form fields). A nonce proves // intent, not authorization — require the capability explicitly. if ( ! current_user_can( 'manage_options' ) ) { wp_die( -1, 403 ); } $this->get_logger()->debug( 'getDetails called', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'post' => $_POST, ] ); if ( isset( $_POST['hash'] ) && wp_verify_nonce( wp_unslash( $_POST['nonce'] ), 'f12_doi_details' ) ) { global $wpdb; $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin'; $hash = sanitize_text_field( $_POST['hash'] ); $this->get_logger()->debug( 'Looking up OptIn by hash', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'hash' => $hash, ] ); $OptIn = OptIn::get_by_hash( $hash ); if ( null == $OptIn ) { $this->get_logger()->warning( 'OptIn not found for hash', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'hash' => $hash, ] ); ob_start(); ?>

200, 'content' => $content ] ); wp_die(); } $this->get_logger()->info( 'OptIn found', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'optin' => [ 'id' => $OptIn->get_id(), 'hash' => $OptIn->get_hash(), 'email' => $OptIn->get_email(), ], ] ); $formfields = maybe_unserialize( $OptIn->get_content() ); // Handle nested content structure (e.g., Avada stores {data: {...}, field_labels: {...}, ...}) if ( is_array( $formfields ) && isset( $formfields['data'] ) && is_array( $formfields['data'] ) && ! isset( $formfields['fields'] ) ) { $formfields = $formfields['data']; } ob_start(); ?>

get_hash() ); ?>

get_id() ); ?>
get_cf_form_id() ); ?>
get_createtime( 'formatted' ) ); ?>
get_ipaddr_register() ); ?>
is_confirmed() ) { echo esc_html( $OptIn->get_updatetime( 'formatted' ) ); } ?>
get_ipaddr_confirmation() ); ?>

$value ): ?> $value ): ?>
200, 'content' => $content ] ); exit; } $this->get_logger()->warning( 'Invalid request for getDetails (missing or invalid nonce/hash)', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'post' => $_POST, ] ); wp_die( 0 ); } /** * Add the styles for the form */ public function addStyles( $hook ) { $this->get_logger()->debug( 'addStyles called', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'hook' => $hook, ] ); if ( $hook == 'tools_page_f12doubleoptin' ) { $ver = defined( 'FORGE12_OPTIN_VERSION' ) ? FORGE12_OPTIN_VERSION : false; wp_enqueue_style( 'f12-cf7-doubleoptin-admin', plugins_url( 'assets/admin-style.css', __FILE__ ), array(), $ver ); wp_enqueue_script( 'f12-cf7-doubleoptin-admin', plugins_url( 'assets/f12-cf7-popup.js', __FILE__ ), [ 'jquery' ], $ver, true ); $this->get_logger()->info( 'Admin styles and scripts enqueued for DOI tools page', [ 'plugin' => 'double-opt-in', 'class' => __CLASS__, 'method' => __METHOD__, 'hook' => $hook, ] ); } } } new Ajax( Logger::getInstance() ); }