loadMissing('organization'); if ($room->status === 'cancelled') { return view('meet.join.cancelled', [ 'room' => $room, 'organization' => $room->organization, ]); } if ($room->isWebinar() && $request->query('token')) { $request->session()->put("meet.webinar.{$room->uuid}", $request->query('token')); } [$allowed, $reason] = $this->security->canJoin($room, $request->user(), $request->user()?->email); if (! $allowed && $reason !== 'passcode_required') { return view('meet.join.denied', [ 'room' => $room, 'organization' => $room->organization, 'reason' => $reason, ]); } if ($room->passcode && ! $request->session()->get("meet.passcode.{$room->uuid}")) { return view('meet.join.passcode', [ 'room' => $room, 'organization' => $room->organization, ]); } $session = $room->activeSession(); if ($session) { $participantUuid = $request->session()->get("meet.participant.{$session->uuid}"); $participant = $participantUuid ? Participant::where('uuid', $participantUuid)->where('session_id', $session->id)->first() : null; if ($participant?->status === 'waiting') { return redirect()->route('meet.join.waiting', $room); } if ($participant?->status === 'joined') { return redirect()->route('meet.room', $session); } } return view('meet.join.show', [ 'room' => $room, 'organization' => $room->organization, 'user' => $request->user(), 'isWebinar' => $room->isWebinar(), ]); } public function verifyPasscode(Request $request, Room $room): RedirectResponse { $request->validate(['passcode' => ['required', 'string']]); if ($room->passcode !== $request->input('passcode')) { return back()->withErrors(['passcode' => 'Incorrect passcode.']); } $request->session()->put("meet.passcode.{$room->uuid}", true); return redirect()->route('meet.join', $room); } public function enter(Request $request, Room $room): RedirectResponse { if ($room->passcode && ! $request->session()->get("meet.passcode.{$room->uuid}")) { return redirect()->route('meet.join', $room); } $rules = [ 'email' => ['nullable', 'email'], ]; if ($request->user()) { $rules['display_name'] = ['nullable', 'string', 'max:100']; } else { $rules['display_name'] = ['required', 'string', 'max:100']; } try { $validated = $request->validate($rules); } catch (ValidationException $e) { throw $e->redirectTo(route('meet.join', $room)); } $user = $request->user(); $displayName = $user?->name ?? ($validated['display_name'] ?? 'Guest'); $email = $user?->email ?? ($validated['email'] ?? null); [$allowed, $reason] = $this->security->canJoin($room, $user, $email ?? $displayName.'@guest.local'); if (! $allowed && $reason !== 'passcode_required') { return back()->withErrors(['join' => $reason ?? 'Unable to join meeting.']); } if ($room->isLive() && $room->activeSession()) { $session = $room->activeSession(); } elseif ($room->isTownHall() && $user && $user->ownerRef() === $room->host_user_ref && $room->setting('green_room')) { $session = app(\App\Services\Meet\TownHallService::class)->startGreenRoom($room, $user); } elseif ($user && $user->ownerRef() === $room->host_user_ref) { $session = $this->sessions->start($room, $user); } elseif ($room->setting('join_before_host') || ($user && $user->ownerRef() === $room->host_user_ref)) { if (! $room->activeSession() && $user && $user->ownerRef() === $room->host_user_ref) { $session = $this->sessions->start($room, $user); } elseif (! $room->activeSession()) { return back()->withErrors(['join' => 'Meeting has not started yet. Please wait for the host.']); } else { $session = $room->activeSession(); } } else { if (! $room->activeSession()) { return back()->withErrors(['join' => 'Meeting has not started yet. Please wait for the host.']); } $session = $room->activeSession(); } $role = ($user && $user->ownerRef() === $room->host_user_ref) ? 'host' : 'guest'; if ($room->isWebinar() && $role !== 'host') { $token = $request->session()->get("meet.webinar.{$room->uuid}"); $reg = $token ? $this->webinars->findByAccessToken($token) : ($email ? $this->webinars->findApprovedRegistration($room, $email) : null); if (! $reg) { return redirect()->route('meet.webinar.register', $room) ->withErrors(['register' => 'Approved registration is required for this webinar.']); } $role = 'attendee'; $displayName = $reg->display_name; } $joinStatus = $room->requiresWaitingRoom($user, $role) ? 'waiting' : 'joined'; $participant = $this->sessions->joinParticipant($session, $user, $role, $displayName, $email, $joinStatus); app(InvitationService::class)->markAcceptedOnJoin($room, $user, $email); $this->sessions->rememberParticipantSession($request, $participant, $session); if ($participant->status === 'waiting') { return redirect()->route('meet.join.waiting', $room); } return redirect()->route('meet.room', $session); } public function waiting(Request $request, Room $room): View|RedirectResponse { $room->loadMissing('organization'); $session = $room->activeSession(); abort_unless($session?->isLive(), 404); $participant = $this->participantFromSession($request, $session); if (! $participant || $participant->status === 'removed') { return redirect()->route('meet.join', $room); } if ($participant->status === 'joined') { return redirect()->route('meet.room', $session); } return view('meet.join.waiting', [ 'room' => $room, 'session' => $session, 'organization' => $room->organization, 'participant' => $participant, ]); } public function waitingStatus(Request $request, Room $room): JsonResponse { $session = $room->activeSession(); abort_unless($session, 404); $participant = $this->participantFromSession($request, $session); return response()->json([ 'status' => $participant?->status ?? 'unknown', 'redirect' => ($participant && $participant->status === 'joined') ? route('meet.room', $session) : null, 'denied' => $participant?->status === 'removed', ]); } protected function participantFromSession(Request $request, Session $session): ?Participant { $participantUuid = $request->session()->get("meet.participant.{$session->uuid}"); if (! $participantUuid) { return null; } return Participant::query() ->where('uuid', $participantUuid) ->where('session_id', $session->id) ->first(); } }