Files
ladill-meet/app/Services/Meet/InvitationService.php
T
isaaccladandCursor 965fb992e9
Deploy Ladill Meet / deploy (push) Failing after 7s
Initial Ladill Meet release.
Phases 0–18: core meetings, webinar, breakouts, team chat, live streaming, town hall, billing, and Ladill Mail calendar wiring.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 23:35:29 +00:00

121 lines
3.5 KiB
PHP

<?php
namespace App\Services\Meet;
use App\Models\Invitation;
use App\Models\Room;
use App\Models\User;
use Illuminate\Support\Str;
class InvitationService
{
public function __construct(
protected MeetNotificationService $notifications,
) {}
/**
* @param array<int, array<string, mixed>> $invites
*/
public function inviteToRoom(Room $room, array $invites, User $host): void
{
$normalized = collect($invites)->map(function ($invite) {
$email = trim((string) ($invite['email'] ?? ''));
if ($email === '') {
return null;
}
$user = User::where('email', $email)->first();
return [
'email' => $email,
'name' => $invite['name'] ?? $invite['display_name'] ?? $user?->name,
'role' => $invite['role'] ?? 'participant',
'user_ref' => $invite['user_ref'] ?? $user?->ownerRef(),
];
})->filter()->unique('email')->values()->all();
$this->notifications->sendInvitations($room, $normalized, $host);
}
public function resend(Invitation $invitation, User $host): Invitation
{
$this->notifications->sendInvitationEmail($invitation->room, $invitation, $host);
$invitation->update(['sent_at' => now()]);
return $invitation->fresh();
}
public function respond(Invitation $invitation, string $status, ?string $token = null): Invitation
{
abort_unless(in_array($status, ['accepted', 'declined'], true), 422);
if ($token !== null) {
abort_unless(hash_equals((string) $invitation->rsvp_token, $token), 403);
}
$invitation->update([
'status' => $status,
'responded_at' => now(),
]);
return $invitation->fresh();
}
public function markAcceptedOnJoin(Room $room, ?User $user, ?string $email): void
{
$query = $room->invitations()->where('status', 'pending');
if ($user) {
$query->where(function ($q) use ($user, $email) {
$q->where('user_ref', $user->ownerRef());
if ($email) {
$q->orWhere('email', $email);
}
});
} elseif ($email) {
$query->where('email', $email);
} else {
return;
}
$query->update(['status' => 'accepted', 'responded_at' => now()]);
}
/**
* @return array<int, array<string, mixed>>
*/
public function parseCsv(string $content): array
{
$lines = preg_split('/\r\n|\r|\n/', trim($content)) ?: [];
$invites = [];
foreach ($lines as $index => $line) {
$line = trim($line);
if ($line === '' || ($index === 0 && str_contains(strtolower($line), 'email'))) {
continue;
}
$parts = str_getcsv($line);
$email = trim((string) ($parts[0] ?? ''));
if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL)) {
continue;
}
$invites[] = [
'email' => $email,
'name' => trim((string) ($parts[1] ?? '')) ?: null,
'role' => trim((string) ($parts[2] ?? '')) ?: 'participant',
];
}
return $invites;
}
public function bulkFromCsv(Room $room, string $csvContent, User $host): int
{
$invites = $this->parseCsv($csvContent);
$this->inviteToRoom($room, $invites, $host);
return count($invites);
}
}