get_subscribers(); if ( isset( $list[ $key ] ) ) { return; // Already subscribed (pending or confirmed). } $doi = ! empty( $opts['reminder_double_optin'] ); $sub = array( 'email' => $email, 'first' => isset( $extra['first'] ) ? (string) $extra['first'] : '', 'token' => wp_generate_password( 20, false ), // unsubscribe token 'added' => current_time( 'mysql' ), 'confirmed' => $doi ? false : true, ); if ( $doi ) { $sub['confirm'] = wp_generate_password( 24, false ); // double-opt-in token } $list[ $key ] = $sub; update_option( self::STORE, $list, false ); if ( $doi ) { $this->send_confirmation( $sub, $opts ); } } /** * Send the double-opt-in confirmation e-mail with an activation button. * * @param array $sub Subscriber record. * @param array $opts Plugin options. */ private function send_confirmation( $sub, $opts ) { if ( ! is_email( $sub['email'] ) || empty( $sub['confirm'] ) ) { return; } $sitename = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); $confirm_url = add_query_arg( array( 'advk_confirm' => 1, 'token' => rawurlencode( $sub['confirm'] ) ), home_url( '/' ) ); $first = isset( $sub['first'] ) && '' !== $sub['first'] ? $sub['first'] : ''; $subject = sprintf( /* translators: %s: site name. */ __( 'Bitte bestätige deine Erinnerungen für %s', 'adventura-interactive-advent-calendar' ), $sitename ); $greet = '' !== $first ? sprintf( /* translators: %s: first name. */ __( 'Hallo %s,', 'adventura-interactive-advent-calendar' ), $first ) : __( 'Hallo,', 'adventura-interactive-advent-calendar' ); $body = $greet . "\n\n" . sprintf( /* translators: %s: site name. */ __( 'du möchtest tägliche Erinnerungen an ein neues Türchen von %s erhalten. Bitte bestätige das mit einem Klick auf den Button unten. Ohne Bestätigung senden wir dir keine Erinnerungen.', 'adventura-interactive-advent-calendar' ), $sitename ) . "\n\n" . __( 'Falls du dich nicht angemeldet hast, ignoriere diese E-Mail einfach.', 'adventura-interactive-advent-calendar' ); $cta = array( 'label' => __( 'Erinnerungen bestätigen', 'adventura-interactive-advent-calendar' ), 'url' => $confirm_url, ); $html = ADVK_Emails::html_wrap( $subject, $body, $opts, '', $cta ); wp_mail( $sub['email'], $subject, $html, ADVK_Emails::mail_headers( $opts, true ) ); } /** * Handle the double-opt-in confirmation link (?advk_confirm=1&token=…). */ public function handle_confirm() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- One-click e-mail confirmation; the per-subscriber token below is the authenticator (verified with hash_equals). if ( empty( $_GET['advk_confirm'] ) || empty( $_GET['token'] ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- See above; secret token verified via hash_equals(). $token = sanitize_text_field( wp_unslash( $_GET['token'] ) ); $list = $this->get_subscribers(); $found = false; foreach ( $list as $key => $sub ) { if ( ! empty( $sub['confirm'] ) && hash_equals( (string) $sub['confirm'], $token ) ) { $list[ $key ]['confirmed'] = true; unset( $list[ $key ]['confirm'] ); $found = true; break; } } if ( $found ) { update_option( self::STORE, $list, false ); } wp_die( esc_html__( 'Danke! Deine Erinnerungen sind jetzt bestätigt. Du kannst dieses Fenster schließen.', 'adventura-interactive-advent-calendar' ), esc_html__( 'Bestätigt', 'adventura-interactive-advent-calendar' ), array( 'response' => 200 ) ); } /* ---------------- unsubscribe ---------------- */ private function unsubscribe_url( $token ) { return add_query_arg( array( 'advk_unsub' => 1, 'token' => rawurlencode( $token ), ), home_url( '/' ) ); } public function handle_unsubscribe() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- One-click email unsubscribe; the per-subscriber token below is the authenticator (verified with hash_equals). if ( empty( $_GET['advk_unsub'] ) || empty( $_GET['token'] ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- See above; secret token verified via hash_equals(). $token = sanitize_text_field( wp_unslash( $_GET['token'] ) ); $list = $this->get_subscribers(); $found = false; foreach ( $list as $key => $sub ) { if ( isset( $sub['token'] ) && hash_equals( (string) $sub['token'], $token ) ) { unset( $list[ $key ] ); $found = true; break; } } if ( $found ) { update_option( self::STORE, $list, false ); } wp_die( esc_html__( 'Du erhältst keine Erinnerungen mehr. Du kannst dieses Fenster schließen.', 'adventura-interactive-advent-calendar' ), esc_html__( 'Abgemeldet', 'adventura-interactive-advent-calendar' ), array( 'response' => 200 ) ); } /* ---------------- sending ---------------- */ public function send_due_reminders() { $opts = ADVK_Settings::get_options(); if ( empty( $opts['reminder_enabled'] ) ) { return; } $day = ADVK_Data::today_day(); if ( $day < 1 || $day > ADVK_Data::door_count() ) { return; // Outside the calendar window. } // Guard against duplicate sends on the same calendar day. $stamp = wp_date( 'Y-m-d' ); if ( get_option( 'advk_reminder_last_sent' ) === $stamp ) { return; } update_option( 'advk_reminder_last_sent', $stamp, false ); $list = $this->get_subscribers(); if ( empty( $list ) ) { return; } // Resolve the prize for today's door for nicer copy. $prize = ''; foreach ( ADVK_Data::get_calendar() as $d ) { if ( (int) $d['day'] === (int) $day ) { $prize = (string) $d['prize']; break; } } $sitename = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); $headers = ADVK_Emails::mail_headers( $opts, true ); $subject_tpl = isset( $opts['reminder_subject'] ) ? $opts['reminder_subject'] : ''; $body_tpl = isset( $opts['reminder_body'] ) ? $opts['reminder_body'] : ''; foreach ( $list as $sub ) { $email = isset( $sub['email'] ) ? $sub['email'] : ''; if ( ! is_email( $email ) ) { continue; } // Double-opt-in: skip subscribers who have not confirmed yet. // (Legacy subscribers without the flag were added under single opt-in.) if ( isset( $sub['confirmed'] ) && ! $sub['confirmed'] ) { continue; } $repl = array( '{first}' => isset( $sub['first'] ) ? $sub['first'] : '', '{day}' => (string) $day, '{prize}' => $prize, '{sitename}' => $sitename, ); $subject = strtr( $subject_tpl, $repl ); $body = strtr( $body_tpl, $repl ); $unsub = '' . esc_html__( 'Keine Erinnerungen mehr erhalten', 'adventura-interactive-advent-calendar' ) . ''; $html = ADVK_Emails::html_wrap( $subject, $body, $opts, $unsub ); wp_mail( $email, $subject, $html, $headers ); } } }