handleFormAction(); } $pendingInvites = $this->invites->findPending(); $registrationPageId = Val::int( get_option( self::OPTION_PAGE, 0 ) ); $registrationPageUrl = $registrationPageId > 0 ? (string) get_permalink( $registrationPageId ) : ''; include USC_PLUGIN_DIR . 'templates/admin/invites.php'; } /** * Handle a posted admin action. Returns the registration link for a freshly * created invite — the only time it can be shown, since just the token's hash * is stored — or an empty string for every other action. */ private function handleFormAction(): string { // Nonce is verified by the caller (renderPage) before this method runs. // phpcs:disable WordPress.Security.NonceVerification.Missing $action = sanitize_key( Val::string( wp_unslash( $_POST['usc_action'] ?? '' ) ) ); if ( 'set_page' === $action ) { update_option( self::OPTION_PAGE, absint( Val::int( $_POST['registration_page_id'] ?? 0 ) ) ); } if ( 'invite' === $action ) { $email = sanitize_email( Val::string( wp_unslash( $_POST['email'] ?? '' ) ) ); if ( is_email( $email ) && false === email_exists( $email ) && null === $this->invites->findPendingByEmail( $email ) ) { $rawToken = wp_generate_password( 32, false ); $this->invites->insert( new Invite( email: $email, token: Invite::hashToken( $rawToken ), invitedBy: get_current_user_id(), ) ); return $this->registrationLink( $rawToken ); } } if ( 'group_invite' === $action ) { $expiresAt = $this->normalizeExpiry( sanitize_text_field( Val::string( wp_unslash( $_POST['expires_at'] ?? '' ) ) ) ); if ( null !== $expiresAt ) { $rawToken = wp_generate_password( 32, false ); $this->invites->insert( new Invite( email: '', token: Invite::hashToken( $rawToken ), invitedBy: get_current_user_id(), kind: Invite::KIND_GROUP, expiresAt: $expiresAt, ) ); return $this->registrationLink( $rawToken ); } } if ( 'revoke' === $action ) { $inviteId = absint( Val::int( $_POST['invite_id'] ?? 0 ) ); if ( $inviteId > 0 ) { $this->invites->revoke( $inviteId ); } } // phpcs:enable WordPress.Security.NonceVerification.Missing return ''; } /** * Validate a submitted group-link expiry date (strict `Y-m-d`, today or * later) and expand it to the end of that day; null when invalid or past. */ private function normalizeExpiry( string $date ): ?string { $day = \DateTimeImmutable::createFromFormat( '!Y-m-d', $date ); if ( false === $day || $day->format( 'Y-m-d' ) !== $date ) { return null; } if ( $date < Val::string( current_time( 'Y-m-d' ) ) ) { return null; } return $date . ' 23:59:59'; } /** * Build the registration URL for a raw invite token. */ private function registrationLink( string $rawToken ): string { $pageId = Val::int( get_option( self::OPTION_PAGE, 0 ) ); $linkBase = $pageId > 0 ? (string) get_permalink( $pageId ) : ''; return add_query_arg( 'us_invite', rawurlencode( $rawToken ), '' !== $linkBase ? $linkBase : home_url( '/' ) ); } }