Privacy and authorization
One rule (Support\EventVisibility) feeds both Event::scopeVisibleTo($viewer) — and with it Events::visibleTo() and Events::browse() — and EventPolicy::view, so a list and a detail page never disagree:
| Who | Draft | Public | Unlisted | Private |
|---|---|---|---|---|
| Managers | ✓ | ✓ | ✓ | ✓ |
| Invitees (pending, accepted) | — | ✓ | ✓ | ✓ |
| Attendees (active RSVP) | — | ✓ | ✓ | ✓ |
| Anyone with the uuid link | — | ✓ | ✓ | — |
| Lists, search, feeds, slug lookups | — | ✓ | managers, invitees, attendees | managers, invitees, attendees |
Unlisted events are link-only: found by uuid, never by slug for strangers, never listed.
What each surface hides
| Surface | Rule |
|---|---|
| browse(), the API index, search | Visible and listed and announced. |
| Route-model binding, Events::find() | By uuid only, never the sequential id or the slug; no visibility rule — authorize view. |
| The API | A hidden event is a 404, exactly like a missing one, on every endpoint. |
| The join link (online_url) | Encrypted at rest; only managers and going attendees (viewOnlineUrl) — API, calendar files, notices; never in feeds or JSON-LD unless expose_online_url. |
| Guest list | guest_list: hidden (managers), attendees (going attendees), public (viewers). Interested people, the check-in record and the status / checked_in / tier filters are managers-only. |
| Counts | Managers, or viewers when show_counts — the event’s counts and seats_left and each ticket type’s seats_left; sort=popularity ranks hidden counts as zero. Whether an event is full is public; the numbers are not. |
| Check-in codes | Encrypted, hash-indexed, never in managers’ lists, domain events, logs or about; the QR is never cached. |
| Calendar files | Events::ics() skips events the viewer may not see; a single event’s ics($viewer) throws the uniform EventNotFoundException; drafts are never exported. |
| JSON-LD and URL templates | Public, announced events only (unlisted with seo.json_ld.unlisted), never private; unlisted and private events never get a {slug} URL. |
| Notices | Recipients filtered by visibility when sent and again when delivered; no other attendee’s data. |
| Public filters | ?organizer= (feed) and filter[organizer] (index) never look the organizer row up — no existence oracle for your users’ keys. |
| API writes | Never without a signed-in Eloquent user (401), whatever events.routes.auth_middleware holds. |
The policy
Policies\EventPolicy and Policies\VenuePolicy are registered unless events.authorization.register_policy is off. Events::for($event)->by($user) checks the policy before every call — the writes and the reads that reveal people; without by() the call is trusted code:
| Ability | Who |
|---|---|
viewAny | Anyone, guests included. |
view | The visibility rule above. |
create | Authenticated, plus events.authorization.abilities.create when set. |
update, delete, restore, publish, unpublish, postpone, reschedule, cancel, complete, transition, moveOnline, manageTiers, invite, manageAttendees, checkIn, viewWaitlist | Managers. |
manageHosts, transfer | The organizer’s approved users, or an admin. |
rsvp | view + the phase and window rules + the private-event invitation rule. |
viewAttendees | Managers; going attendees with guest_list = attendees; viewers with public. |
viewInterested | Managers. |
viewCounts | Managers, or viewers with show_counts. |
viewOnlineUrl | Managers and going attendees. |
An undefined ability denies — with events.admin undefined, nobody is an admin. Swap the policy with events.authorization.policy, extending the package’s class. With registration off and no policy of yours, by() still never silently allows: the configured class is asked directly.
Venues
Anyone views a venue, and any organizer may hold an event at any venue. Creating needs an authenticated user, plus abilities.venues when set. An owned venue is managed by its owner’s approved users and admins and created only by someone the owner allows, so nobody plants a venue into another team’s list. A shared venue sits under other organizers’ events, so only admins and holders of abilities.shared_venues create, edit, geocode or delete one.
Visitor IPs
Geolocation::locateRequest($request), the usual near-me starting point, sends the visitor’s IP address to geolocation-for-laravel’s IP providers. Mention it in your privacy notice, or ask for the location instead.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.