Features

Markdown

Collaborative item editing

When two editors open the same collection item, Externa shows who else is on the form, optional soft lock (read-only for others until takeover), and a stale-save warning if the row changed on the server. This is a Directus-style collaboration signal — not CRDT field sync.

Requires Reverb

Presence and live lock hints use Laravel Echo on presence channel collection-item.{collectionId}.{itemId}. Configure Reverb & Echo (BROADCAST_CONNECTION=reverb, reverb:start, and Vite VITE_REVERB_*). When realtime is off, the item form still sends expected_updated_at on save so the API can reject stale overwrites; avatars and lock whispers are degraded.

Related

Item CRUD: Collection items. Team chat (separate channel): Chat. Global online users: Reverb & Echo.

Behavior

FeatureRealtime onRealtime off (degraded)
Other editors’ avatarsEcho here / joining / leaving on collection-item.*Hidden; short banner notes limited mode
Soft lockWhisper item-lock + heartbeat (~30s TTL)Not enforced client-side; server ACL unchanged
Stale overwriteWhisper item-updated-at + dialog before submitexpected_updated_at hidden field + server validation

Editors with collection access join the presence channel when the item form mounts. The first active editor claims a soft lock; others see read-only fields until Take over editing. Saving after a remote change prompts Reload or Save anyway; matching expected_updated_at is required unless the user confirms.

Authorization

Channel auth: routes/channels.php — active user, can-show-collections, item exists, CollectionPermissionEnforcer::isItemReadable. Same read gate as opening the item editor.

Manual test checklist

  • [ ] Two browsers, same item: each sees the other’s avatar in the form header.
  • [ ] Second browser: fields read-only until takeover; takeover restores edit.
  • [ ] Browser A saves; browser B save shows stale dialog (with Reverb) or validation error (without).
  • [ ] User without collection access cannot authorize the presence channel (403 on /broadcasting/auth).

Code map

AreaLocation
Channelroutes/channels.php (collection-item.{collectionId}.{itemId})
Client presenceresources/js/lib/item-presence.ts, hooks/use-item-presence.ts
UIcomponents/collections/item-editing-presence.tsx, pages/collections/items/form.tsx
Stale guardItemController::update + expected_updated_at on UpdateCollectionItemRequest
Teststests/Feature/Collections/CollectionItemPresenceChannelTest.php, CollectionItemStaleUpdateTest.php