Project invitations
View on GitHubProject invitations
The root README.md
already narrates the whole invitation flow end to end (token, 7-day
expiry, the logged-in-vs-not accept paths, why the invitation email
either goes through the normal notification pipeline or a dedicated
one-time ProjectInvitationMail depending on whether the address
already has an account). This category doesn't repeat that — it's the
"how do I extend this" companion, since the flow itself doesn't have
one anywhere yet.
Guides, in the order you'd actually need them
- Invite multiple emails at once — worked example turning the single-email invite form into a bulk one, tracing the change through the hook, the component, the validation, and the Service loop.
Two related, already-documented pieces worth knowing about before
extending this flow further:
../permissions/03-grant-a-custom-role-in-bulk.md
covers the custom-role-preassignment side of an invitation, and
../notifications/04-add-a-dedicated-transactional-email.md
covers ProjectInvitationMail's own pattern for a dedicated,
preference-independent email.
The architecture in one paragraph
ProjectInvitationService::invite() is the single entry point: it
checks MailConfigurationService::isEnabled() first (no working
outgoing mail, no invitations — the entire feature is gated on it),
deletes any existing pending invitation for the same email/project
pair (so re-inviting someone is just calling invite() again — there
is no separate "resend" action or endpoint), creates a new
ProjectInvitation row with a random 64-character token and a 7-day
expires_at, optionally attaches pre-assigned custom Roles via the
project_invitation_role pivot, and fires ProjectInvited (consumed
by SendNotificationListener — see
../notifications/README.md). Accepting
one (acceptByToken()) attaches the user to the project with the
invitation's base RoleType, syncs their system role, attaches any
pre-assigned custom roles, and marks the invitation accepted_at —
called either automatically right after login/register (if the
pending_invitation_token session key from a not-logged-in click on
the email link is present) or manually by pasting the token into the
Members tab's JoinWithCodePanel.
