0 && $unit_id > 0 ? get_userdata( $user_id ) : false; if ( ! $user instanceof \WP_User ) { return null; } global $wpdb; $hash = Tokens::email_hash( (string) $user->user_email ); $proven = VerifiedEmail::woocommerce_verified( $user_id ) ? SqlPart::of( '1' ) : SqlPart::of( 'EXISTS ( SELECT 1 FROM %i pv WHERE pv.user_id = %d AND pv.email_verified = %s )', Db::table( 'people' ), $user_id, $hash ); return Db::row( SqlPart::of( 'SELECT /* outside-tx */ i.id AS invitee_id, i.invitation_id, i.user_id, i.answer, i.token_hash, i.token_seed, i.token_expires_at, v.kind, v.course_id, v.unit_id, v.occurrence_id, v.rsvp, v.rsvp_until, v.yes_action, v.places, v.state, o.starts_at, o.ends_at FROM %i i JOIN %i v ON v.id = i.invitation_id LEFT JOIN %i o ON o.id = v.occurrence_id WHERE v.course_id = %d AND v.unit_id = %d AND v.kind = %d AND v.state <> %d AND i.state = %d AND i.token_hash IS NOT NULL AND ( i.user_id = %d OR ( i.user_id = 0 AND i.email_hash = %s AND ', Db::table( 'invitees' ), Db::table( 'invitations' ), Db::table( 'live_occurrences' ), $course_id, $unit_id, InvitationKind::RSVP->value, InvitationState::CANCELLED->value, InviteeState::SENT->value, $user_id, $hash )->add( $proven )->add( ' ) ) ORDER BY v.id DESC LIMIT 1' ) ); } public static function card( int $course_id, int $unit_id, int $user_id ): string { $row = self::row( $course_id, $unit_id, $user_id ); if ( null === $row ) { return ''; } $current = RsvpAnswer::tryFrom( (int) $row['answer'] ) ?? RsvpAnswer::NONE; $token = Tokens::for_invitee( (int) $row['invitee_id'], (string) ( $row['token_seed'] ?? '' ) ); $valid = '' !== $token && hash_equals( (string) $row['token_hash'], Tokens::hash( $token ) ); $open = $valid && RsvpService::open( $row ); $html = '
'; $html .= '

' . esc_html__( 'Your invitation', 'rooster-lms' ) . '

'; if ( RsvpAnswer::NONE !== $current ) { /* translators: %s: the answer (Yes, No, Maybe). */ $html .= '

' . esc_html( sprintf( __( 'Your answer: %s', 'rooster-lms' ), $current->label() ) ) . '

'; } if ( ! $open ) { $html .= RsvpAnswer::NONE === $current ? '

' . esc_html__( 'Answers to this invitation are closed.', 'rooster-lms' ) . '

' : ''; return $html . '
'; } $html .= '
' . '' . RsvpPage::nonce_input( RsvpRoute::nonce_action( $token ) ) . '' . '

' . esc_html( RsvpAnswer::NONE === $current ? __( 'Will you attend?', 'rooster-lms' ) : __( 'Changed your mind? Choose again:', 'rooster-lms' ) ) . '

'; foreach ( RsvpAnswer::offered( (int) $row['rsvp'] ) as $answer ) { $variant = RsvpAnswer::YES === $answer ? 'primary' : 'outline'; $html .= ' '; } $html .= '

'; $until = RsvpService::closes_at_row( $row ); if ( '' !== $until ) { /* translators: %s: date and time. */ $html .= '

' . esc_html( sprintf( __( 'You can change your answer until %s.', 'rooster-lms' ), (string) wp_date( (string) get_option( 'date_format', 'j F Y' ) . ' ' . (string) get_option( 'time_format', 'H:i' ), (int) strtotime( $until . ' UTC' ) ) ) ) . '

'; } return $html . ''; } }