diff --git a/code_samples/collaboration/CollaborationController.php b/code_samples/collaboration/CollaborationController.php
new file mode 100644
index 0000000000..a60c357746
--- /dev/null
+++ b/code_samples/collaboration/CollaborationController.php
@@ -0,0 +1,261 @@
+contentService->loadContent($contentId);
+
+ $requestData = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
+
+ $sessionStruct = new SessionCreateStruct();
+ $sessionStruct->contentId = $contentId;
+ $sessionStruct->name = $requestData['name'] ?? 'Collaboration Session';
+
+ if (isset($requestData['expires_at'])) {
+ $sessionStruct->expiresAt = new \DateTime($requestData['expires_at']);
+ }
+
+ $session = $this->collaborationService->createSession($sessionStruct);
+
+ return $this->json([
+ 'success' => true,
+ 'session' => [
+ 'id' => $session->id,
+ 'name' => $session->name,
+ 'status' => $session->status,
+ 'content_id' => $session->contentId,
+ 'created_at' => $session->createdAt->format('c'),
+ ],
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->json([
+ 'success' => false,
+ 'error' => $e->getMessage(),
+ ], Response::HTTP_BAD_REQUEST);
+ }
+ }
+
+ #[Route('/collaboration/session/{sessionId}/invite', name: 'collaboration_invite_user', methods: ['POST'])]
+ public function inviteUser(int $sessionId, Request $request): JsonResponse
+ {
+ try {
+ $session = $this->collaborationService->getSession($sessionId);
+ $requestData = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
+
+ $invitationStruct = new InvitationCreateStruct();
+ $invitationStruct->sessionId = $sessionId;
+ $invitationStruct->email = $requestData['email'];
+ $invitationStruct->role = $requestData['role'] ?? 'editor';
+ $invitationStruct->message = $requestData['message'] ?? '';
+
+ if (isset($requestData['expires_at'])) {
+ $invitationStruct->expiresAt = new \DateTime($requestData['expires_at']);
+ }
+
+ $invitation = $this->invitationService->createInvitation($invitationStruct);
+
+ return $this->json([
+ 'success' => true,
+ 'invitation' => [
+ 'id' => $invitation->id,
+ 'email' => $invitation->email,
+ 'role' => $invitation->role,
+ 'status' => $invitation->status,
+ 'created_at' => $invitation->createdAt->format('c'),
+ ],
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->json([
+ 'success' => false,
+ 'error' => $e->getMessage(),
+ ], Response::HTTP_BAD_REQUEST);
+ }
+ }
+
+ #[Route('/collaboration/session/{sessionId}/participants', name: 'collaboration_get_participants', methods: ['GET'])]
+ public function getParticipants(int $sessionId): JsonResponse
+ {
+ try {
+ $participants = $this->participantService->getParticipants($sessionId);
+
+ $participantData = [];
+ foreach ($participants as $participant) {
+ $user = $this->userService->loadUser($participant->userId);
+
+ $participantData[] = [
+ 'id' => $participant->id,
+ 'user_id' => $participant->userId,
+ 'user_name' => $user->getName(),
+ 'role' => $participant->role,
+ 'permissions' => $participant->permissions,
+ 'joined_at' => $participant->joinedAt->format('c'),
+ 'last_activity' => $participant->lastActivity?->format('c'),
+ ];
+ }
+
+ return $this->json([
+ 'success' => true,
+ 'participants' => $participantData,
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->json([
+ 'success' => false,
+ 'error' => $e->getMessage(),
+ ], Response::HTTP_BAD_REQUEST);
+ }
+ }
+
+ #[Route('/collaboration/session/{sessionId}/join', name: 'collaboration_join_session', methods: ['POST'])]
+ public function joinSession(int $sessionId): JsonResponse
+ {
+ try {
+ $session = $this->collaborationService->getSession($sessionId);
+ $currentUser = $this->userService->loadUser($this->getUser()->getUserIdentifier());
+
+ $participantStruct = new ParticipantCreateStruct();
+ $participantStruct->userId = $currentUser->id;
+ $participantStruct->role = 'editor';
+ $participantStruct->permissions = [
+ 'edit' => true,
+ 'comment' => true,
+ 'invite' => false,
+ ];
+
+ $participant = $this->participantService->addParticipant($session, $participantStruct);
+
+ return $this->json([
+ 'success' => true,
+ 'participant' => [
+ 'id' => $participant->id,
+ 'role' => $participant->role,
+ 'permissions' => $participant->permissions,
+ 'joined_at' => $participant->joinedAt->format('c'),
+ ],
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->json([
+ 'success' => false,
+ 'error' => $e->getMessage(),
+ ], Response::HTTP_BAD_REQUEST);
+ }
+ }
+
+ #[Route('/collaboration/session/{sessionId}/end', name: 'collaboration_end_session', methods: ['POST'])]
+ public function endSession(int $sessionId): JsonResponse
+ {
+ try {
+ $this->collaborationService->endSession($sessionId);
+
+ return $this->json([
+ 'success' => true,
+ 'message' => 'Session ended successfully',
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->json([
+ 'success' => false,
+ 'error' => $e->getMessage(),
+ ], Response::HTTP_BAD_REQUEST);
+ }
+ }
+
+ #[Route('/collaboration/my-sessions', name: 'collaboration_my_sessions', methods: ['GET'])]
+ public function getMySessions(): JsonResponse
+ {
+ try {
+ $currentUser = $this->userService->loadUser($this->getUser()->getUserIdentifier());
+
+ // Get sessions where user is owner
+ $query = new \Ibexa\Contracts\Collaboration\Values\Session\Query\SessionQuery();
+ $query->filter = new \Ibexa\Contracts\Collaboration\Values\Session\Query\Criterion\OwnerId($currentUser->id);
+ $query->sortClauses = [
+ new \Ibexa\Contracts\Collaboration\Values\Session\Query\SortClause\UpdatedAt(\Ibexa\Contracts\Core\Repository\Values\Content\Query::SORT_DESC),
+ ];
+
+ $ownedSessions = $this->collaborationService->findSessions($query);
+
+ // Get sessions where user is participant
+ $participantQuery = new \Ibexa\Contracts\Collaboration\Values\Session\Query\SessionQuery();
+ $participantQuery->filter = new \Ibexa\Contracts\Collaboration\Values\Session\Query\Criterion\ParticipantId($currentUser->id);
+
+ $participantSessions = $this->collaborationService->findSessions($participantQuery);
+
+ return $this->json([
+ 'success' => true,
+ 'owned_sessions' => $this->formatSessionsData($ownedSessions->sessions),
+ 'participant_sessions' => $this->formatSessionsData($participantSessions->sessions),
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->json([
+ 'success' => false,
+ 'error' => $e->getMessage(),
+ ], Response::HTTP_BAD_REQUEST);
+ }
+ }
+
+ private function formatSessionsData(array $sessions): array
+ {
+ $data = [];
+
+ foreach ($sessions as $session) {
+ try {
+ $content = $this->contentService->loadContent($session->contentId);
+
+ $data[] = [
+ 'id' => $session->id,
+ 'name' => $session->name,
+ 'status' => $session->status,
+ 'content' => [
+ 'id' => $content->id,
+ 'name' => $content->getName(),
+ 'content_type' => $content->getContentType()->getName(),
+ ],
+ 'created_at' => $session->createdAt->format('c'),
+ 'expires_at' => $session->expiresAt?->format('c'),
+ 'participant_count' => count($this->participantService->getParticipants($session->id)),
+ ];
+ } catch (\Exception $e) {
+ // Skip sessions with inaccessible content
+ continue;
+ }
+ }
+
+ return $data;
+ }
+}
\ No newline at end of file
diff --git a/code_samples/collaboration/CollaborationEventSubscriber.php b/code_samples/collaboration/CollaborationEventSubscriber.php
new file mode 100644
index 0000000000..8ac37c6ba4
--- /dev/null
+++ b/code_samples/collaboration/CollaborationEventSubscriber.php
@@ -0,0 +1,306 @@
+ ['onSessionCreated', 10],
+ SessionEndedEvent::class => ['onSessionEnded', 10],
+ ParticipantAddedEvent::class => ['onParticipantAdded', 10],
+ ParticipantRemovedEvent::class => ['onParticipantRemoved', 10],
+ InvitationCreatedEvent::class => ['onInvitationCreated', 10],
+ InvitationAcceptedEvent::class => ['onInvitationAccepted', 10],
+ ];
+ }
+
+ public function onSessionCreated(SessionCreatedEvent $event): void
+ {
+ $session = $event->getSession();
+
+ try {
+ $content = $this->contentService->loadContent($session->contentId);
+ $owner = $this->userService->loadUser($session->ownerId);
+
+ $this->logger->info('Collaboration session created', [
+ 'session_id' => $session->id,
+ 'content_id' => $session->contentId,
+ 'content_name' => $content->getName(),
+ 'owner_id' => $session->ownerId,
+ 'owner_name' => $owner->getName(),
+ ]);
+
+ // Send notification email to owner
+ $this->sendSessionCreatedEmail($session, $owner, $content);
+
+ } catch (\Exception $e) {
+ $this->logger->error('Error handling session created event', [
+ 'session_id' => $session->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ public function onSessionEnded(SessionEndedEvent $event): void
+ {
+ $session = $event->getSession();
+
+ try {
+ $this->logger->info('Collaboration session ended', [
+ 'session_id' => $session->id,
+ 'content_id' => $session->contentId,
+ 'duration' => $session->createdAt->diff(new \DateTime())->format('%d days %h hours'),
+ ]);
+
+ // Clean up session data if needed
+ $this->cleanupSessionData($session);
+
+ } catch (\Exception $e) {
+ $this->logger->error('Error handling session ended event', [
+ 'session_id' => $session->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ public function onParticipantAdded(ParticipantAddedEvent $event): void
+ {
+ $participant = $event->getParticipant();
+ $session = $event->getSession();
+
+ try {
+ $user = $this->userService->loadUser($participant->userId);
+ $content = $this->contentService->loadContent($session->contentId);
+
+ $this->logger->info('Participant added to collaboration session', [
+ 'session_id' => $session->id,
+ 'participant_id' => $participant->id,
+ 'user_id' => $participant->userId,
+ 'user_name' => $user->getName(),
+ 'role' => $participant->role,
+ ]);
+
+ // Send welcome email to new participant
+ $this->sendParticipantWelcomeEmail($participant, $session, $user, $content);
+
+ } catch (\Exception $e) {
+ $this->logger->error('Error handling participant added event', [
+ 'session_id' => $session->id,
+ 'participant_id' => $participant->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ public function onParticipantRemoved(ParticipantRemovedEvent $event): void
+ {
+ $participant = $event->getParticipant();
+ $session = $event->getSession();
+
+ try {
+ $this->logger->info('Participant removed from collaboration session', [
+ 'session_id' => $session->id,
+ 'participant_id' => $participant->id,
+ 'user_id' => $participant->userId,
+ 'role' => $participant->role,
+ ]);
+
+ } catch (\Exception $e) {
+ $this->logger->error('Error handling participant removed event', [
+ 'session_id' => $session->id,
+ 'participant_id' => $participant->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ public function onInvitationCreated(InvitationCreatedEvent $event): void
+ {
+ $invitation = $event->getInvitation();
+
+ try {
+ $session = $event->getSession();
+ $content = $this->contentService->loadContent($session->contentId);
+ $owner = $this->userService->loadUser($session->ownerId);
+
+ $this->logger->info('Collaboration invitation created', [
+ 'invitation_id' => $invitation->id,
+ 'session_id' => $invitation->sessionId,
+ 'email' => $invitation->email,
+ 'role' => $invitation->role,
+ ]);
+
+ // Send invitation email
+ $this->sendInvitationEmail($invitation, $session, $owner, $content);
+
+ } catch (\Exception $e) {
+ $this->logger->error('Error handling invitation created event', [
+ 'invitation_id' => $invitation->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ public function onInvitationAccepted(InvitationAcceptedEvent $event): void
+ {
+ $invitation = $event->getInvitation();
+ $participant = $event->getParticipant();
+
+ try {
+ $session = $event->getSession();
+ $user = $this->userService->loadUser($participant->userId);
+
+ $this->logger->info('Collaboration invitation accepted', [
+ 'invitation_id' => $invitation->id,
+ 'session_id' => $invitation->sessionId,
+ 'participant_id' => $participant->id,
+ 'user_name' => $user->getName(),
+ ]);
+
+ // Notify session owner about accepted invitation
+ $this->sendInvitationAcceptedEmail($invitation, $session, $user);
+
+ } catch (\Exception $e) {
+ $this->logger->error('Error handling invitation accepted event', [
+ 'invitation_id' => $invitation->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ private function sendSessionCreatedEmail($session, $owner, $content): void
+ {
+ try {
+ $email = (new Email())
+ ->from($this->fromEmail)
+ ->to($owner->email)
+ ->subject('Collaboration Session Created')
+ ->html($this->twig->render('emails/collaboration/session_created.html.twig', [
+ 'session' => $session,
+ 'owner' => $owner,
+ 'content' => $content,
+ ]));
+
+ $this->mailer->send($email);
+ } catch (\Exception $e) {
+ $this->logger->error('Failed to send session created email', [
+ 'session_id' => $session->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ private function sendParticipantWelcomeEmail($participant, $session, $user, $content): void
+ {
+ try {
+ $email = (new Email())
+ ->from($this->fromEmail)
+ ->to($user->email)
+ ->subject('Welcome to Collaboration Session')
+ ->html($this->twig->render('emails/collaboration/participant_welcome.html.twig', [
+ 'participant' => $participant,
+ 'session' => $session,
+ 'user' => $user,
+ 'content' => $content,
+ ]));
+
+ $this->mailer->send($email);
+ } catch (\Exception $e) {
+ $this->logger->error('Failed to send participant welcome email', [
+ 'participant_id' => $participant->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ private function sendInvitationEmail($invitation, $session, $owner, $content): void
+ {
+ try {
+ $invitationUrl = sprintf(
+ 'https://example.com/collaboration/invitation/accept/%s',
+ $invitation->token
+ );
+
+ $email = (new Email())
+ ->from($this->fromEmail)
+ ->to($invitation->email)
+ ->subject(sprintf('Invitation to Collaborate on "%s"', $content->getName()))
+ ->html($this->twig->render('emails/collaboration/invitation.html.twig', [
+ 'invitation' => $invitation,
+ 'session' => $session,
+ 'owner' => $owner,
+ 'content' => $content,
+ 'invitation_url' => $invitationUrl,
+ ]));
+
+ $this->mailer->send($email);
+ } catch (\Exception $e) {
+ $this->logger->error('Failed to send invitation email', [
+ 'invitation_id' => $invitation->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ private function sendInvitationAcceptedEmail($invitation, $session, $user): void
+ {
+ try {
+ $owner = $this->userService->loadUser($session->ownerId);
+
+ $email = (new Email())
+ ->from($this->fromEmail)
+ ->to($owner->email)
+ ->subject('Collaboration Invitation Accepted')
+ ->html($this->twig->render('emails/collaboration/invitation_accepted.html.twig', [
+ 'invitation' => $invitation,
+ 'session' => $session,
+ 'user' => $user,
+ 'owner' => $owner,
+ ]));
+
+ $this->mailer->send($email);
+ } catch (\Exception $e) {
+ $this->logger->error('Failed to send invitation accepted email', [
+ 'invitation_id' => $invitation->id,
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ private function cleanupSessionData($session): void
+ {
+ // Implement session cleanup logic here
+ // For example: remove temporary files, clear cache, etc.
+ $this->logger->info('Cleaning up session data', [
+ 'session_id' => $session->id,
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/docs/api/event_reference/collaboration_events.md b/docs/api/event_reference/collaboration_events.md
new file mode 100644
index 0000000000..dd9306d245
--- /dev/null
+++ b/docs/api/event_reference/collaboration_events.md
@@ -0,0 +1,340 @@
+---
+description: Events related to collaborative editing features, including sessions, participants, and invitations.
+---
+
+# Collaboration events
+
+Collaboration events are dispatched during various collaboration activities, allowing you to customize behavior and integrate with external systems.
+
+## Session events
+
+### Session lifecycle
+
+| Event | Dispatched when |
+|-------|----------------|
+| `Ibexa\Contracts\Collaboration\Event\Session\BeforeSessionCreateEvent` | Before creating a collaboration session |
+| `Ibexa\Contracts\Collaboration\Event\Session\SessionCreatedEvent` | After a session is created |
+| `Ibexa\Contracts\Collaboration\Event\Session\BeforeSessionUpdateEvent` | Before updating session properties |
+| `Ibexa\Contracts\Collaboration\Event\Session\SessionUpdatedEvent` | After session properties are updated |
+| `Ibexa\Contracts\Collaboration\Event\Session\BeforeSessionEndEvent` | Before ending a session |
+| `Ibexa\Contracts\Collaboration\Event\Session\SessionEndedEvent` | After a session is ended |
+
+### Session activity
+
+| Event | Dispatched when |
+|-------|----------------|
+| `Ibexa\Contracts\Collaboration\Event\Session\SessionExpiredEvent` | When a session expires automatically |
+| `Ibexa\Contracts\Collaboration\Event\Session\SessionSuspendedEvent` | When a session is suspended |
+| `Ibexa\Contracts\Collaboration\Event\Session\SessionResumedEvent` | When a suspended session is resumed |
+
+## Participant events
+
+### Participant management
+
+| Event | Dispatched when |
+|-------|----------------|
+| `Ibexa\Contracts\Collaboration\Event\Participant\BeforeParticipantAddEvent` | Before adding a participant |
+| `Ibexa\Contracts\Collaboration\Event\Participant\ParticipantAddedEvent` | After a participant is added |
+| `Ibexa\Contracts\Collaboration\Event\Participant\BeforeParticipantRemoveEvent` | Before removing a participant |
+| `Ibexa\Contracts\Collaboration\Event\Participant\ParticipantRemovedEvent` | After a participant is removed |
+| `Ibexa\Contracts\Collaboration\Event\Participant\ParticipantUpdatedEvent` | When participant role or permissions change |
+
+### Participant activity
+
+| Event | Dispatched when |
+|-------|----------------|
+| `Ibexa\Contracts\Collaboration\Event\Participant\ParticipantJoinedEvent` | When a participant joins an active session |
+| `Ibexa\Contracts\Collaboration\Event\Participant\ParticipantLeftEvent` | When a participant leaves a session |
+| `Ibexa\Contracts\Collaboration\Event\Participant\ParticipantActivityEvent` | When a participant performs an action |
+
+## Invitation events
+
+### Invitation lifecycle
+
+| Event | Dispatched when |
+|-------|----------------|
+| `Ibexa\Contracts\Collaboration\Event\Invitation\InvitationCreatedEvent` | After an invitation is sent |
+| `Ibexa\Contracts\Collaboration\Event\Invitation\InvitationAcceptedEvent` | When an invitation is accepted |
+| `Ibexa\Contracts\Collaboration\Event\Invitation\InvitationDeclinedEvent` | When an invitation is declined |
+| `Ibexa\Contracts\Collaboration\Event\Invitation\InvitationExpiredEvent` | When an invitation expires |
+| `Ibexa\Contracts\Collaboration\Event\Invitation\InvitationCancelledEvent` | When an invitation is cancelled |
+
+### Invitation reminders
+
+| Event | Dispatched when |
+|-------|----------------|
+| `Ibexa\Contracts\Collaboration\Event\Invitation\InvitationReminderEvent` | When sending invitation reminders |
+| `Ibexa\Contracts\Collaboration\Event\Invitation\InvitationFollowUpEvent` | For follow-up actions on invitations |
+
+## Real-time editing events
+
+### Content synchronization
+
+| Event | Dispatched when |
+|-------|----------------|
+| `Ibexa\Contracts\Collaboration\Event\RealTime\ContentChangedEvent` | When content is modified during collaboration |
+| `Ibexa\Contracts\Collaboration\Event\RealTime\ConflictDetectedEvent` | When conflicting changes are detected |
+| `Ibexa\Contracts\Collaboration\Event\RealTime\ConflictResolvedEvent` | After conflicts are resolved |
+| `Ibexa\Contracts\Collaboration\Event\RealTime\SyncCompletedEvent` | After content synchronization |
+
+### User presence
+
+| Event | Dispatched when |
+|-------|----------------|
+| `Ibexa\Contracts\Collaboration\Event\RealTime\ParticipantPresenceEvent` | When participant presence changes |
+| `Ibexa\Contracts\Collaboration\Event\RealTime\CursorMovedEvent` | When a participant's cursor moves |
+| `Ibexa\Contracts\Collaboration\Event\RealTime\UserConnectedEvent` | When a user connects to real-time session |
+| `Ibexa\Contracts\Collaboration\Event\RealTime\UserDisconnectedEvent` | When a user disconnects from session |
+
+## Notification events
+
+### Email notifications
+
+| Event | Dispatched when |
+|-------|----------------|
+| `Ibexa\Contracts\Collaboration\Event\Notification\EmailNotificationEvent` | Before sending email notifications |
+| `Ibexa\Contracts\Collaboration\Event\Notification\EmailSentEvent` | After an email is sent |
+| `Ibexa\Contracts\Collaboration\Event\Notification\EmailFailedEvent` | When email delivery fails |
+
+### System notifications
+
+| Event | Dispatched when |
+|-------|----------------|
+| `Ibexa\Contracts\Collaboration\Event\Notification\SystemNotificationEvent` | For in-app notifications |
+| `Ibexa\Contracts\Collaboration\Event\Notification\NotificationReadEvent` | When a notification is read |
+| `Ibexa\Contracts\Collaboration\Event\Notification\NotificationDismissedEvent` | When a notification is dismissed |
+
+## Usage examples
+
+### Basic event listener
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Session\SessionCreatedEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+class CollaborationEventSubscriber implements EventSubscriberInterface
+{
+ public static function getSubscribedEvents(): array
+ {
+ return [
+ SessionCreatedEvent::class => 'onSessionCreated',
+ ];
+ }
+
+ public function onSessionCreated(SessionCreatedEvent $event): void
+ {
+ $session = $event->getSession();
+ // Your custom logic here
+ }
+}
+```
+
+### Event listener with attributes
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Participant\ParticipantAddedEvent;
+use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
+
+class NotificationService
+{
+ #[AsEventListener(event: ParticipantAddedEvent::class)]
+ public function onParticipantAdded(ParticipantAddedEvent $event): void
+ {
+ $participant = $event->getParticipant();
+ $session = $event->getSession();
+
+ // Send welcome notification
+ $this->sendWelcomeNotification($participant, $session);
+ }
+}
+```
+
+### Preventable events
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Session\BeforeSessionEndEvent;
+
+#[AsEventListener(event: BeforeSessionEndEvent::class)]
+public function onBeforeSessionEnd(BeforeSessionEndEvent $event): void
+{
+ $session = $event->getSession();
+
+ // Check if session can be ended
+ if ($this->hasUnsavedChanges($session)) {
+ // Prevent the session from ending
+ $event->stopPropagation();
+
+ // Optionally set a reason
+ $event->setErrorMessage('Session has unsaved changes');
+ }
+}
+```
+
+## Event data access
+
+### Session events
+
+```php
+public function onSessionCreated(SessionCreatedEvent $event): void
+{
+ $session = $event->getSession(); // CollaborationSession
+ $sessionStruct = $event->getSessionCreateStruct(); // SessionCreateStruct (in BeforeSessionCreateEvent)
+
+ // Access session data
+ $sessionId = $session->id;
+ $contentId = $session->contentId;
+ $ownerId = $session->ownerId;
+ $status = $session->status;
+}
+```
+
+### Participant events
+
+```php
+public function onParticipantAdded(ParticipantAddedEvent $event): void
+{
+ $participant = $event->getParticipant(); // Participant
+ $session = $event->getSession(); // CollaborationSession
+
+ // Access participant data
+ $userId = $participant->userId;
+ $role = $participant->role;
+ $permissions = $participant->permissions;
+}
+```
+
+### Invitation events
+
+```php
+public function onInvitationCreated(InvitationCreatedEvent $event): void
+{
+ $invitation = $event->getInvitation(); // Invitation
+ $session = $event->getSession(); // CollaborationSession
+
+ // Access invitation data
+ $email = $invitation->email;
+ $role = $invitation->role;
+ $token = $invitation->token;
+ $expiresAt = $invitation->expiresAt;
+}
+```
+
+## Integration with other systems
+
+### Workflow integration
+
+```php
+use Ibexa\Contracts\Workflow\Event\Action\ActionExecutedEvent;
+use Ibexa\Contracts\Collaboration\Event\Session\SessionCreatedEvent;
+
+#[AsEventListener(event: ActionExecutedEvent::class)]
+public function onWorkflowAction(ActionExecutedEvent $event): void
+{
+ if ($event->getActionName() === 'collaboration_review') {
+ // Create collaboration session from workflow
+ $this->createCollaborationFromWorkflow($event->getWorkflowItem());
+ }
+}
+
+#[AsEventListener(event: SessionCreatedEvent::class)]
+public function onSessionCreated(SessionCreatedEvent $event): void
+{
+ $session = $event->getSession();
+
+ // Update workflow if session was created from workflow
+ if (isset($session->metadata['workflow_item_id'])) {
+ $this->updateWorkflowProgress($session);
+ }
+}
+```
+
+### Search integration
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Session\SessionEndedEvent;
+
+#[AsEventListener(event: SessionEndedEvent::class)]
+public function onSessionEnded(SessionEndedEvent $event): void
+{
+ $session = $event->getSession();
+
+ // Update search index with collaboration metadata
+ $this->updateContentSearchIndex($session);
+}
+```
+
+## Custom events
+
+You can create custom events for specific collaboration scenarios:
+
+```php
+use Ibexa\Contracts\Collaboration\Event\AbstractCollaborationEvent;
+
+class CustomCollaborationEvent extends AbstractCollaborationEvent
+{
+ public function __construct(
+ private CollaborationSession $session,
+ private array $customData
+ ) {
+ }
+
+ public function getSession(): CollaborationSession
+ {
+ return $this->session;
+ }
+
+ public function getCustomData(): array
+ {
+ return $this->customData;
+ }
+}
+
+// Dispatch custom event
+$this->eventDispatcher->dispatch(
+ new CustomCollaborationEvent($session, $customData)
+);
+```
+
+## Performance considerations
+
+### Async event processing
+
+For heavy operations, consider using message queues:
+
+```php
+use Symfony\Component\Messenger\MessageBusInterface;
+
+#[AsEventListener(event: SessionCreatedEvent::class)]
+public function onSessionCreated(SessionCreatedEvent $event): void
+{
+ // Queue heavy operations
+ $this->messageBus->dispatch(new ProcessSessionCreated($event->getSession()->id));
+}
+```
+
+### Event batching
+
+For high-frequency events, implement batching:
+
+```php
+class BatchedEventProcessor
+{
+ private array $eventQueue = [];
+
+ #[AsEventListener(event: ParticipantActivityEvent::class)]
+ public function queueParticipantActivity(ParticipantActivityEvent $event): void
+ {
+ $this->eventQueue[] = $event;
+
+ if (count($this->eventQueue) >= 10) {
+ $this->processBatch();
+ }
+ }
+}
+```
+
+## See also
+
+- [Collaborative editing guide](../../content_management/collaborative_editing/collaborative_editing_guide.md)
+- [Collaborative editing API](../../content_management/collaborative_editing/collaborative_editing_api.md)
+- [Event reference](event_reference.md)
\ No newline at end of file
diff --git a/docs/content_management/collaborative_editing/collaborative_editing_api.md b/docs/content_management/collaborative_editing/collaborative_editing_api.md
new file mode 100644
index 0000000000..5b5f2205c2
--- /dev/null
+++ b/docs/content_management/collaborative_editing/collaborative_editing_api.md
@@ -0,0 +1,627 @@
+---
+description: Use the collaborative editing PHP API to integrate collaboration features into your applications.
+---
+
+# Collaborative editing PHP API
+
+The collaborative editing feature provides a comprehensive PHP API for managing collaboration sessions, participants, invitations, and real-time synchronization.
+
+## Core services
+
+### CollaborationService
+
+The main service for managing collaboration sessions.
+
+```php
+use Ibexa\Contracts\Collaboration\CollaborationServiceInterface;
+
+class MyCollaborationController
+{
+ public function __construct(
+ private CollaborationServiceInterface $collaborationService
+ ) {}
+
+ public function createSession(Content $content): CollaborationSession
+ {
+ $sessionCreateStruct = new SessionCreateStruct();
+ $sessionCreateStruct->contentId = $content->id;
+ $sessionCreateStruct->name = 'Review Session';
+ $sessionCreateStruct->expiresAt = new \DateTime('+1 day');
+
+ return $this->collaborationService->createSession($sessionCreateStruct);
+ }
+}
+```
+
+#### Key methods
+
+| Method | Description |
+|--------|-------------|
+| `createSession(SessionCreateStruct $struct)` | Create a new collaboration session |
+| `getSession(int $sessionId)` | Retrieve session by ID |
+| `updateSession(CollaborationSession $session, SessionUpdateStruct $struct)` | Update session properties |
+| `endSession(int $sessionId)` | End an active session |
+| `findSessions(SessionQuery $query)` | Search for sessions |
+
+### ParticipantService
+
+Manages session participants and their permissions.
+
+```php
+use Ibexa\Contracts\Collaboration\ParticipantServiceInterface;
+
+public function addParticipant(
+ CollaborationSession $session,
+ User $user,
+ string $role = 'editor'
+): Participant {
+ $participantStruct = new ParticipantCreateStruct();
+ $participantStruct->userId = $user->id;
+ $participantStruct->role = $role;
+ $participantStruct->permissions = [
+ 'edit' => true,
+ 'comment' => true,
+ 'invite' => false,
+ ];
+
+ return $this->participantService->addParticipant($session, $participantStruct);
+}
+```
+
+#### Participant management methods
+
+| Method | Description |
+|--------|-------------|
+| `addParticipant(CollaborationSession $session, ParticipantCreateStruct $struct)` | Add user to session |
+| `removeParticipant(int $sessionId, int $userId)` | Remove participant |
+| `updateParticipant(Participant $participant, ParticipantUpdateStruct $struct)` | Update participant role/permissions |
+| `getParticipants(int $sessionId)` | List session participants |
+| `isParticipant(int $sessionId, int $userId)` | Check if user is participant |
+
+### InvitationService
+
+Handles invitation creation and management.
+
+```php
+use Ibexa\Contracts\Collaboration\InvitationServiceInterface;
+
+public function inviteUser(CollaborationSession $session, string $email): Invitation
+{
+ $invitationStruct = new InvitationCreateStruct();
+ $invitationStruct->sessionId = $session->id;
+ $invitationStruct->email = $email;
+ $invitationStruct->role = 'reviewer';
+ $invitationStruct->message = 'Please review this content';
+ $invitationStruct->expiresAt = new \DateTime('+7 days');
+
+ return $this->invitationService->createInvitation($invitationStruct);
+}
+```
+
+#### Invitation methods
+
+| Method | Description |
+|--------|-------------|
+| `createInvitation(InvitationCreateStruct $struct)` | Send invitation |
+| `acceptInvitation(string $token)` | Accept invitation by token |
+| `declineInvitation(string $token)` | Decline invitation |
+| `cancelInvitation(int $invitationId)` | Cancel pending invitation |
+| `findInvitations(InvitationQuery $query)` | Search invitations |
+
+## Value objects
+
+### CollaborationSession
+
+Represents a collaboration session.
+
+```php
+use Ibexa\Contracts\Collaboration\Values\Session\CollaborationSession;
+
+class CollaborationSession extends ValueObject
+{
+ public readonly int $id;
+ public readonly int $contentId;
+ public readonly int $ownerId;
+ public readonly string $name;
+ public readonly string $status;
+ public readonly \DateTimeInterface $createdAt;
+ public readonly ?\DateTimeInterface $expiresAt;
+ public readonly array $metadata;
+}
+```
+
+#### Session status values
+
+- `active` - Session is currently active
+- `expired` - Session has expired
+- `ended` - Session was manually ended
+- `suspended` - Session is temporarily suspended
+
+### Participant
+
+Represents a session participant.
+
+```php
+use Ibexa\Contracts\Collaboration\Values\Participant\Participant;
+
+class Participant extends ValueObject
+{
+ public readonly int $id;
+ public readonly int $sessionId;
+ public readonly int $userId;
+ public readonly string $role;
+ public readonly array $permissions;
+ public readonly \DateTimeInterface $joinedAt;
+ public readonly ?\DateTimeInterface $lastActivity;
+}
+```
+
+#### Participant roles
+
+- `owner` - Session owner with full control
+- `admin` - Can manage session and participants
+- `editor` - Can edit content and comment
+- `reviewer` - Can review, comment, and approve
+- `viewer` - Read-only access with commenting
+
+### Invitation
+
+Represents a collaboration invitation.
+
+```php
+use Ibexa\Contracts\Collaboration\Values\Invitation\Invitation;
+
+class Invitation extends ValueObject
+{
+ public readonly int $id;
+ public readonly int $sessionId;
+ public readonly string $email;
+ public readonly ?int $userId;
+ public readonly string $token;
+ public readonly string $status;
+ public readonly string $role;
+ public readonly \DateTimeInterface $createdAt;
+ public readonly ?\DateTimeInterface $expiresAt;
+}
+```
+
+## Query and search
+
+### SessionQuery
+
+Search for collaboration sessions.
+
+```php
+use Ibexa\Contracts\Collaboration\Values\Session\Query\SessionQuery;
+use Ibexa\Contracts\Collaboration\Values\Session\Query\Criterion;
+use Ibexa\Contracts\Collaboration\Values\Session\Query\SortClause;
+
+$query = new SessionQuery();
+$query->filter = new Criterion\LogicalAnd([
+ new Criterion\Status(['active']),
+ new Criterion\OwnerId($currentUserId),
+ new Criterion\ContentType(['article', 'blog_post']),
+]);
+
+$query->sortClauses = [
+ new SortClause\UpdatedAt(Query::SORT_DESC),
+];
+
+$query->limit = 20;
+$query->offset = 0;
+
+$sessions = $this->collaborationService->findSessions($query);
+```
+
+#### Available criteria
+
+| Criterion | Description |
+|-----------|-------------|
+| `Status` | Filter by session status |
+| `OwnerId` | Filter by session owner |
+| `ParticipantId` | Sessions where user is participant |
+| `ContentId` | Filter by content ID |
+| `ContentType` | Filter by content type |
+| `DateRange` | Filter by date range |
+
+#### Sort clauses
+
+| Sort Clause | Description |
+|-------------|-------------|
+| `CreatedAt` | Sort by creation date |
+| `UpdatedAt` | Sort by last update |
+| `ExpiresAt` | Sort by expiration date |
+| `Name` | Sort alphabetically by name |
+
+### InvitationQuery
+
+Search for invitations.
+
+```php
+use Ibexa\Contracts\Collaboration\Values\Invitation\Query\InvitationQuery;
+use Ibexa\Contracts\Collaboration\Values\Invitation\Query\Criterion as InvitationCriterion;
+
+$query = new InvitationQuery();
+$query->filter = new InvitationCriterion\LogicalAnd([
+ new InvitationCriterion\Status(['pending']),
+ new InvitationCriterion\Email($userEmail),
+]);
+
+$invitations = $this->invitationService->findInvitations($query);
+```
+
+## Real-time editing API
+
+### RealTimeEditingService
+
+Manages real-time collaboration features.
+
+```php
+use Ibexa\Contracts\Collaboration\RealTimeEditingServiceInterface;
+
+public function enableRealTimeEditing(CollaborationSession $session): void
+{
+ $this->realTimeService->enableRealTime($session->id, [
+ 'sync_interval' => 1000, // milliseconds
+ 'conflict_resolution' => 'last_write_wins',
+ 'cursor_tracking' => true,
+ ]);
+}
+
+public function syncChanges(int $sessionId, array $changes): array
+{
+ return $this->realTimeService->syncChanges($sessionId, $changes);
+}
+```
+
+### WebSocket integration
+
+```php
+use Ibexa\Contracts\Collaboration\WebSocket\CollaborationWebSocketInterface;
+
+public function broadcastChange(int $sessionId, array $change): void
+{
+ $this->webSocketService->broadcast($sessionId, [
+ 'type' => 'content_change',
+ 'user_id' => $this->currentUser->id,
+ 'change' => $change,
+ 'timestamp' => time(),
+ ]);
+}
+```
+
+## Events and listeners
+
+### Collaboration events
+
+The system dispatches various events during collaboration:
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Session\BeforeSessionCreateEvent;
+use Ibexa\Contracts\Collaboration\Event\Session\SessionCreatedEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+class CollaborationEventSubscriber implements EventSubscriberInterface
+{
+ public static function getSubscribedEvents(): array
+ {
+ return [
+ BeforeSessionCreateEvent::class => 'onBeforeSessionCreate',
+ SessionCreatedEvent::class => 'onSessionCreated',
+ ];
+ }
+
+ public function onBeforeSessionCreate(BeforeSessionCreateEvent $event): void
+ {
+ $struct = $event->getSessionCreateStruct();
+
+ // Add custom validation
+ if (empty($struct->name)) {
+ $struct->name = 'Collaboration Session ' . date('Y-m-d H:i');
+ }
+ }
+
+ public function onSessionCreated(SessionCreatedEvent $event): void
+ {
+ $session = $event->getSession();
+
+ // Send notification to content owner
+ $this->notificationService->notify(
+ $session->ownerId,
+ 'collaboration_session_created',
+ ['session' => $session]
+ );
+ }
+}
+```
+
+#### Available events
+
+**Session events**:
+- `BeforeSessionCreateEvent` / `SessionCreatedEvent`
+- `BeforeSessionUpdateEvent` / `SessionUpdatedEvent`
+- `BeforeSessionEndEvent` / `SessionEndedEvent`
+
+**Participant events**:
+- `BeforeParticipantAddEvent` / `ParticipantAddedEvent`
+- `BeforeParticipantRemoveEvent` / `ParticipantRemovedEvent`
+- `ParticipantUpdatedEvent`
+
+**Invitation events**:
+- `InvitationCreatedEvent`
+- `InvitationAcceptedEvent` / `InvitationDeclinedEvent`
+- `InvitationExpiredEvent`
+
+## Custom integrations
+
+### Custom session types
+
+Create custom session types for specific workflows:
+
+```php
+use Ibexa\Contracts\Collaboration\Values\Session\SessionCreateStruct;
+
+class ReviewSessionCreateStruct extends SessionCreateStruct
+{
+ public array $reviewers = [];
+ public ?\DateTime $reviewDeadline = null;
+ public bool $requireAllApprovals = false;
+}
+
+class ReviewCollaborationService
+{
+ public function createReviewSession(
+ Content $content,
+ array $reviewers,
+ \DateTime $deadline
+ ): CollaborationSession {
+ $struct = new ReviewSessionCreateStruct();
+ $struct->contentId = $content->id;
+ $struct->reviewers = $reviewers;
+ $struct->reviewDeadline = $deadline;
+ $struct->requireAllApprovals = true;
+
+ $session = $this->collaborationService->createSession($struct);
+
+ // Auto-invite reviewers
+ foreach ($reviewers as $reviewerId) {
+ $this->addReviewer($session, $reviewerId);
+ }
+
+ return $session;
+ }
+}
+```
+
+### Integration with workflow
+
+Connect collaboration with content workflows:
+
+```php
+use Ibexa\Contracts\Workflow\Event\Action\ActionExecutedEvent;
+
+class WorkflowCollaborationIntegration
+{
+ public function onWorkflowAction(ActionExecutedEvent $event): void
+ {
+ $workflowItem = $event->getWorkflowItem();
+
+ if ($event->getActionName() === 'collaboration_review') {
+ $content = $this->contentService->loadContent(
+ $workflowItem->getSubject()->id
+ );
+
+ // Create collaboration session for review stage
+ $session = $this->createCollaborationFromWorkflow($content, $workflowItem);
+
+ // Auto-assign reviewers based on workflow metadata
+ $this->assignWorkflowReviewers($session, $workflowItem);
+ }
+ }
+}
+```
+
+### REST API extension
+
+Extend the REST API for collaboration features:
+
+```php
+use Ibexa\Contracts\Rest\Output\ValueObjectVisitor;
+use Ibexa\Contracts\Rest\Output\Generator;
+use Ibexa\Contracts\Rest\Output\Visitor;
+
+class CollaborationSessionValueObjectVisitor extends ValueObjectVisitor
+{
+ public function visit(Visitor $visitor, Generator $generator, $data)
+ {
+ $generator->startObjectElement('CollaborationSession');
+
+ $generator->startValueElement('id', $data->id);
+ $generator->endValueElement('id');
+
+ $generator->startValueElement('contentId', $data->contentId);
+ $generator->endValueElement('contentId');
+
+ // Add participants
+ $generator->startObjectElement('participants');
+ foreach ($data->participants as $participant) {
+ $visitor->visitValueObject($participant);
+ }
+ $generator->endObjectElement('participants');
+
+ $generator->endObjectElement('CollaborationSession');
+ }
+}
+```
+
+## Performance optimization
+
+### Caching strategies
+
+```php
+use Symfony\Contracts\Cache\CacheInterface;
+
+class CachedCollaborationService
+{
+ public function __construct(
+ private CollaborationServiceInterface $collaborationService,
+ private CacheInterface $cache
+ ) {}
+
+ public function getSession(int $sessionId): CollaborationSession
+ {
+ return $this->cache->get(
+ "collaboration_session_{$sessionId}",
+ function () use ($sessionId) {
+ return $this->collaborationService->getSession($sessionId);
+ }
+ );
+ }
+
+ public function invalidateSessionCache(int $sessionId): void
+ {
+ $this->cache->delete("collaboration_session_{$sessionId}");
+ }
+}
+```
+
+### Batch operations
+
+```php
+public function createBulkInvitations(
+ CollaborationSession $session,
+ array $emails,
+ string $role = 'reviewer'
+): array {
+ $invitations = [];
+
+ // Process in batches to avoid memory issues
+ $batches = array_chunk($emails, 50);
+
+ foreach ($batches as $batch) {
+ $batchInvitations = $this->invitationService->createBulkInvitations(
+ $session->id,
+ $batch,
+ $role
+ );
+
+ $invitations = array_merge($invitations, $batchInvitations);
+ }
+
+ return $invitations;
+}
+```
+
+## Error handling
+
+### Custom exceptions
+
+```php
+use Ibexa\Contracts\Collaboration\Exception\CollaborationException;
+
+class SessionExpiredException extends CollaborationException
+{
+ public function __construct(CollaborationSession $session)
+ {
+ parent::__construct(
+ "Collaboration session {$session->id} has expired"
+ );
+ }
+}
+
+class InsufficientPermissionsException extends CollaborationException
+{
+ public function __construct(string $permission, int $userId)
+ {
+ parent::__construct(
+ "User {$userId} does not have '{$permission}' permission"
+ );
+ }
+}
+```
+
+### Exception handling in controllers
+
+```php
+public function joinSessionAction(int $sessionId): Response
+{
+ try {
+ $session = $this->collaborationService->getSession($sessionId);
+
+ if (!$session->isActive()) {
+ throw new SessionExpiredException($session);
+ }
+
+ $participant = $this->participantService->addCurrentUserToSession($session);
+
+ return $this->json(['success' => true, 'participant' => $participant]);
+
+ } catch (SessionExpiredException $e) {
+ return $this->json(['error' => 'Session has expired'], 410);
+ } catch (InsufficientPermissionsException $e) {
+ return $this->json(['error' => 'Access denied'], 403);
+ } catch (CollaborationException $e) {
+ return $this->json(['error' => $e->getMessage()], 400);
+ }
+}
+```
+
+## Testing collaboration features
+
+### Unit testing
+
+```php
+use PHPUnit\Framework\TestCase;
+use Ibexa\Contracts\Collaboration\CollaborationServiceInterface;
+
+class CollaborationServiceTest extends TestCase
+{
+ public function testCreateSession(): void
+ {
+ $content = $this->createMockContent();
+
+ $struct = new SessionCreateStruct();
+ $struct->contentId = $content->id;
+ $struct->name = 'Test Session';
+
+ $session = $this->collaborationService->createSession($struct);
+
+ $this->assertEquals($content->id, $session->contentId);
+ $this->assertEquals('Test Session', $session->name);
+ $this->assertEquals('active', $session->status);
+ }
+}
+```
+
+### Integration testing
+
+```php
+use Ibexa\Tests\Integration\Core\Repository\BaseTest;
+
+class CollaborationIntegrationTest extends BaseTest
+{
+ public function testFullCollaborationWorkflow(): void
+ {
+ // Create content
+ $content = $this->createTestContent();
+
+ // Create session
+ $session = $this->createTestSession($content);
+
+ // Invite participant
+ $invitation = $this->inviteTestUser($session);
+
+ // Accept invitation
+ $participant = $this->acceptInvitation($invitation);
+
+ // Test collaboration
+ $this->assertTrue($session->isParticipant($participant->userId));
+ $this->assertEquals('editor', $participant->role);
+ }
+}
+```
+
+## Next steps
+
+- [Explore collaboration events](collaborative_editing_events.md)
+- [View PHP API reference](../../api/php_api/php_api_reference/namespaces/ibexa-contracts-collaboration.html)
\ No newline at end of file
diff --git a/docs/content_management/collaborative_editing/collaborative_editing_events.md b/docs/content_management/collaborative_editing/collaborative_editing_events.md
new file mode 100644
index 0000000000..cdad6f4fe9
--- /dev/null
+++ b/docs/content_management/collaborative_editing/collaborative_editing_events.md
@@ -0,0 +1,657 @@
+---
+description: Learn about collaboration events and how to customize collaborative editing behavior.
+---
+
+# Collaborative editing events
+
+The collaborative editing system dispatches various events that allow you to customize behavior, add custom logic, and integrate with external systems.
+
+## Session events
+
+### Session lifecycle events
+
+#### BeforeSessionCreateEvent
+
+Dispatched before creating a collaboration session.
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Session\BeforeSessionCreateEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+class SessionEventSubscriber implements EventSubscriberInterface
+{
+ public static function getSubscribedEvents(): array
+ {
+ return [
+ BeforeSessionCreateEvent::class => ['onBeforeSessionCreate', 10],
+ ];
+ }
+
+ public function onBeforeSessionCreate(BeforeSessionCreateEvent $event): void
+ {
+ $struct = $event->getSessionCreateStruct();
+
+ // Auto-generate session name if not provided
+ if (empty($struct->name)) {
+ $content = $this->contentService->loadContent($struct->contentId);
+ $struct->name = "Collaboration on: " . $content->getName();
+ }
+
+ // Set default expiration
+ if (!$struct->expiresAt) {
+ $struct->expiresAt = new \DateTime('+7 days');
+ }
+
+ // Add metadata
+ $struct->metadata['creator_ip'] = $this->request->getClientIp();
+ $struct->metadata['created_via'] = 'web_interface';
+ }
+}
+```
+
+#### SessionCreatedEvent
+
+Dispatched after a session is successfully created.
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Session\SessionCreatedEvent;
+
+public function onSessionCreated(SessionCreatedEvent $event): void
+{
+ $session = $event->getSession();
+
+ // Send notification to content stakeholders
+ $content = $this->contentService->loadContent($session->contentId);
+ $stakeholders = $this->getContentStakeholders($content);
+
+ foreach ($stakeholders as $stakeholder) {
+ $this->notificationService->send($stakeholder, 'session_created', [
+ 'session' => $session,
+ 'content' => $content,
+ ]);
+ }
+
+ // Log session creation for audit
+ $this->logger->info('Collaboration session created', [
+ 'session_id' => $session->id,
+ 'content_id' => $session->contentId,
+ 'owner_id' => $session->ownerId,
+ ]);
+}
+```
+
+#### BeforeSessionUpdateEvent / SessionUpdatedEvent
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Session\BeforeSessionUpdateEvent;
+use Ibexa\Contracts\Collaboration\Event\Session\SessionUpdatedEvent;
+
+public function onBeforeSessionUpdate(BeforeSessionUpdateEvent $event): void
+{
+ $session = $event->getSession();
+ $struct = $event->getSessionUpdateStruct();
+
+ // Validate session updates
+ if ($struct->expiresAt && $struct->expiresAt < new \DateTime()) {
+ throw new \InvalidArgumentException('Cannot set expiration date in the past');
+ }
+
+ // Track what's being changed
+ $changes = [];
+ if (isset($struct->name) && $struct->name !== $session->name) {
+ $changes['name'] = ['old' => $session->name, 'new' => $struct->name];
+ }
+
+ $event->setMetadata('changes', $changes);
+}
+
+public function onSessionUpdated(SessionUpdatedEvent $event): void
+{
+ $session = $event->getSession();
+ $changes = $event->getMetadata('changes', []);
+
+ if (!empty($changes)) {
+ // Notify participants of changes
+ $this->notifyParticipantsOfUpdate($session, $changes);
+
+ // Log the update
+ $this->auditLogger->info('Session updated', [
+ 'session_id' => $session->id,
+ 'changes' => $changes,
+ ]);
+ }
+}
+```
+
+#### BeforeSessionEndEvent / SessionEndedEvent
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Session\BeforeSessionEndEvent;
+use Ibexa\Contracts\Collaboration\Event\Session\SessionEndedEvent;
+
+public function onBeforeSessionEnd(BeforeSessionEndEvent $event): void
+{
+ $session = $event->getSession();
+
+ // Archive session data before ending
+ $this->archiveService->archiveSession($session);
+
+ // Check for unsaved changes
+ $unsavedChanges = $this->checkForUnsavedChanges($session);
+ if ($unsavedChanges) {
+ // Optionally prevent session ending
+ // $event->stopPropagation();
+
+ // Or warn the user
+ $event->setMetadata('unsaved_changes', $unsavedChanges);
+ }
+}
+
+public function onSessionEnded(SessionEndedEvent $event): void
+{
+ $session = $event->getSession();
+
+ // Notify all participants
+ $participants = $this->participantService->getParticipants($session->id);
+ foreach ($participants as $participant) {
+ $this->notificationService->send($participant->userId, 'session_ended', [
+ 'session' => $session,
+ ]);
+ }
+
+ // Generate session report
+ $report = $this->reportService->generateSessionReport($session);
+ $this->emailService->sendSessionReport($session->ownerId, $report);
+}
+```
+
+## Participant events
+
+### ParticipantAddedEvent
+
+Dispatched when a user joins a collaboration session.
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Participant\ParticipantAddedEvent;
+
+public function onParticipantAdded(ParticipantAddedEvent $event): void
+{
+ $participant = $event->getParticipant();
+ $session = $event->getSession();
+
+ // Welcome new participant
+ $this->notificationService->send($participant->userId, 'welcome_to_session', [
+ 'session' => $session,
+ 'role' => $participant->role,
+ ]);
+
+ // Notify other participants
+ $otherParticipants = $this->participantService->getOtherParticipants(
+ $session->id,
+ $participant->userId
+ );
+
+ foreach ($otherParticipants as $other) {
+ $this->notificationService->send($other->userId, 'participant_joined', [
+ 'session' => $session,
+ 'new_participant' => $participant,
+ ]);
+ }
+
+ // Initialize real-time presence if enabled
+ if ($this->isRealTimeEnabled($session)) {
+ $this->realTimeService->addParticipantPresence($participant);
+ }
+}
+```
+
+### ParticipantRemovedEvent
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Participant\ParticipantRemovedEvent;
+
+public function onParticipantRemoved(ParticipantRemovedEvent $event): void
+{
+ $participant = $event->getParticipant();
+ $session = $event->getSession();
+
+ // Clean up participant data
+ $this->cleanupParticipantData($participant);
+
+ // Remove from real-time presence
+ if ($this->isRealTimeEnabled($session)) {
+ $this->realTimeService->removeParticipantPresence($participant);
+ }
+
+ // Notify remaining participants
+ $remainingParticipants = $this->participantService->getParticipants($session->id);
+ foreach ($remainingParticipants as $other) {
+ $this->notificationService->send($other->userId, 'participant_left', [
+ 'session' => $session,
+ 'removed_participant' => $participant,
+ ]);
+ }
+}
+```
+
+## Invitation events
+
+### InvitationCreatedEvent
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Invitation\InvitationCreatedEvent;
+
+public function onInvitationCreated(InvitationCreatedEvent $event): void
+{
+ $invitation = $event->getInvitation();
+ $session = $this->collaborationService->getSession($invitation->sessionId);
+
+ // Send invitation email
+ $this->emailService->sendInvitationEmail($invitation, [
+ 'session' => $session,
+ 'inviter' => $this->userService->loadUser($session->ownerId),
+ 'invitation_url' => $this->generateInvitationUrl($invitation),
+ ]);
+
+ // Track invitation metrics
+ $this->metricsService->incrementCounter('collaboration.invitations.sent', [
+ 'session_id' => $session->id,
+ 'role' => $invitation->role,
+ 'user_type' => $invitation->userId ? 'internal' : 'external',
+ ]);
+}
+```
+
+### InvitationAcceptedEvent / InvitationDeclinedEvent
+
+```php
+use Ibexa\Contracts\Collaboration\Event\Invitation\InvitationAcceptedEvent;
+use Ibexa\Contracts\Collaboration\Event\Invitation\InvitationDeclinedEvent;
+
+public function onInvitationAccepted(InvitationAcceptedEvent $event): void
+{
+ $invitation = $event->getInvitation();
+ $participant = $event->getParticipant();
+ $session = $this->collaborationService->getSession($invitation->sessionId);
+
+ // Track acceptance
+ $this->metricsService->incrementCounter('collaboration.invitations.accepted');
+
+ // Send welcome message
+ $this->notificationService->send($participant->userId, 'invitation_accepted', [
+ 'session' => $session,
+ 'participant' => $participant,
+ ]);
+
+ // Update session activity
+ $this->updateSessionActivity($session);
+}
+
+public function onInvitationDeclined(InvitationDeclinedEvent $event): void
+{
+ $invitation = $event->getInvitation();
+ $session = $this->collaborationService->getSession($invitation->sessionId);
+
+ // Track declination
+ $this->metricsService->incrementCounter('collaboration.invitations.declined');
+
+ // Notify session owner
+ $this->notificationService->send($session->ownerId, 'invitation_declined', [
+ 'invitation' => $invitation,
+ 'session' => $session,
+ ]);
+}
+```
+
+## Real-time editing events
+
+### ContentChangedEvent
+
+```php
+use Ibexa\Contracts\Collaboration\Event\RealTime\ContentChangedEvent;
+
+public function onContentChanged(ContentChangedEvent $event): void
+{
+ $change = $event->getChange();
+ $session = $event->getSession();
+ $userId = $event->getUserId();
+
+ // Broadcast change to other participants via WebSocket
+ $this->webSocketService->broadcast($session->id, [
+ 'type' => 'content_change',
+ 'user_id' => $userId,
+ 'change' => $change,
+ 'timestamp' => time(),
+ ], [$userId]); // Exclude the user who made the change
+
+ // Store change history
+ $this->changeHistoryService->recordChange($session, $change, $userId);
+
+ // Check for conflicts
+ if ($this->hasConflict($change, $session)) {
+ $this->conflictResolutionService->handleConflict($session, $change);
+ }
+}
+```
+
+### ParticipantPresenceEvent
+
+```php
+use Ibexa\Contracts\Collaboration\Event\RealTime\ParticipantPresenceEvent;
+
+public function onParticipantPresence(ParticipantPresenceEvent $event): void
+{
+ $presence = $event->getPresence();
+ $session = $event->getSession();
+
+ // Update participant last activity
+ $this->participantService->updateLastActivity($presence->participantId);
+
+ // Broadcast presence to other participants
+ $this->webSocketService->broadcast($session->id, [
+ 'type' => 'participant_presence',
+ 'participant_id' => $presence->participantId,
+ 'cursor_position' => $presence->cursorPosition,
+ 'is_active' => $presence->isActive,
+ ], [$presence->participantId]);
+}
+```
+
+## Content workflow integration events
+
+### WorkflowActionExecutedEvent integration
+
+```php
+use Ibexa\Contracts\Workflow\Event\Action\ActionExecutedEvent;
+use Ibexa\Contracts\Collaboration\Event\Session\SessionCreatedEvent;
+
+public function onWorkflowActionExecuted(ActionExecutedEvent $event): void
+{
+ if ($event->getActionName() === 'request_collaboration_review') {
+ $workflowItem = $event->getWorkflowItem();
+ $content = $this->contentService->loadContent($workflowItem->getSubject()->id);
+
+ // Create collaboration session for workflow review
+ $sessionStruct = new SessionCreateStruct();
+ $sessionStruct->contentId = $content->id;
+ $sessionStruct->name = "Workflow Review: " . $content->getName();
+ $sessionStruct->metadata = [
+ 'workflow_item_id' => $workflowItem->getId(),
+ 'workflow_name' => $workflowItem->getWorkflowName(),
+ 'created_from' => 'workflow_action',
+ ];
+
+ $session = $this->collaborationService->createSession($sessionStruct);
+
+ // Auto-invite reviewers based on workflow metadata
+ $reviewers = $workflowItem->getMetadata('reviewers', []);
+ foreach ($reviewers as $reviewerId) {
+ $this->inviteUserToSession($session, $reviewerId, 'reviewer');
+ }
+ }
+}
+```
+
+## Custom event examples
+
+### SessionActivityEvent
+
+Create custom events for specific use cases:
+
+```php
+use Ibexa\Contracts\Collaboration\Event\AbstractCollaborationEvent;
+
+class SessionActivityEvent extends AbstractCollaborationEvent
+{
+ public function __construct(
+ private CollaborationSession $session,
+ private string $activityType,
+ private array $activityData = []
+ ) {}
+
+ public function getSession(): CollaborationSession
+ {
+ return $this->session;
+ }
+
+ public function getActivityType(): string
+ {
+ return $this->activityType;
+ }
+
+ public function getActivityData(): array
+ {
+ return $this->activityData;
+ }
+}
+
+// Usage
+$this->eventDispatcher->dispatch(
+ new SessionActivityEvent($session, 'content_updated', [
+ 'field' => 'title',
+ 'old_value' => 'Old Title',
+ 'new_value' => 'New Title',
+ ])
+);
+```
+
+### NotificationEvent integration
+
+```php
+use Ibexa\Contracts\Notifications\Event\NotificationEvent;
+
+public function onSessionActivity(SessionActivityEvent $event): void
+{
+ $session = $event->getSession();
+ $activity = $event->getActivityType();
+
+ // Create notification for session activity
+ $notificationEvent = new NotificationEvent(
+ 'collaboration_activity',
+ $session->ownerId,
+ [
+ 'session_id' => $session->id,
+ 'activity_type' => $activity,
+ 'activity_data' => $event->getActivityData(),
+ ]
+ );
+
+ $this->eventDispatcher->dispatch($notificationEvent);
+}
+```
+
+## Event listener registration
+
+### Via service configuration
+
+```yaml
+# config/services.yaml
+services:
+ App\EventSubscriber\CollaborationEventSubscriber:
+ tags:
+ - name: kernel.event_subscriber
+
+ App\EventListener\SessionNotificationListener:
+ tags:
+ - name: kernel.event_listener
+ event: Ibexa\Contracts\Collaboration\Event\Session\SessionCreatedEvent
+ method: onSessionCreated
+ priority: 100
+```
+
+### Via PHP attributes
+
+```php
+use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
+
+class CollaborationNotificationService
+{
+ #[AsEventListener(event: SessionCreatedEvent::class, priority: 100)]
+ public function onSessionCreated(SessionCreatedEvent $event): void
+ {
+ // Handle session created
+ }
+
+ #[AsEventListener(event: ParticipantAddedEvent::class)]
+ public function onParticipantAdded(ParticipantAddedEvent $event): void
+ {
+ // Handle participant added
+ }
+}
+```
+
+## Event debugging and monitoring
+
+### Debug event listeners
+
+```bash
+# List all collaboration event listeners
+php bin/console debug:event-dispatcher | grep -i collaboration
+
+# Debug specific event
+php bin/console debug:event-dispatcher SessionCreatedEvent
+```
+
+### Event monitoring
+
+```php
+use Psr\Log\LoggerInterface;
+
+class CollaborationEventMonitor implements EventSubscriberInterface
+{
+ public function __construct(
+ private LoggerInterface $logger
+ ) {}
+
+ public static function getSubscribedEvents(): array
+ {
+ return [
+ // Monitor all collaboration events with low priority
+ SessionCreatedEvent::class => ['logEvent', -100],
+ ParticipantAddedEvent::class => ['logEvent', -100],
+ InvitationCreatedEvent::class => ['logEvent', -100],
+ ];
+ }
+
+ public function logEvent($event): void
+ {
+ $eventName = get_class($event);
+ $data = $this->extractEventData($event);
+
+ $this->logger->info("Collaboration event: {$eventName}", $data);
+ }
+
+ private function extractEventData($event): array
+ {
+ $data = [];
+
+ if (method_exists($event, 'getSession')) {
+ $data['session_id'] = $event->getSession()->id;
+ }
+
+ if (method_exists($event, 'getParticipant')) {
+ $data['participant_id'] = $event->getParticipant()->id;
+ }
+
+ return $data;
+ }
+}
+```
+
+## Performance considerations
+
+### Async event processing
+
+```php
+use Symfony\Component\Messenger\MessageBusInterface;
+
+class AsyncCollaborationEventHandler
+{
+ public function __construct(
+ private MessageBusInterface $messageBus
+ ) {}
+
+ public function onSessionCreated(SessionCreatedEvent $event): void
+ {
+ // Process heavy operations asynchronously
+ $this->messageBus->dispatch(new SessionCreatedMessage(
+ $event->getSession()->id
+ ));
+ }
+}
+
+// Message handler
+use Symfony\Component\Messenger\Attribute\AsMessageHandler;
+
+#[AsMessageHandler]
+class SessionCreatedMessageHandler
+{
+ public function __invoke(SessionCreatedMessage $message): void
+ {
+ $session = $this->collaborationService->getSession($message->sessionId);
+
+ // Perform heavy operations
+ $this->generateSessionAnalytics($session);
+ $this->updateSearchIndex($session);
+ $this->syncWithExternalSystem($session);
+ }
+}
+```
+
+### Event batching
+
+```php
+class BatchCollaborationEventProcessor
+{
+ private array $eventQueue = [];
+
+ public function queueEvent($event): void
+ {
+ $this->eventQueue[] = $event;
+
+ if (count($this->eventQueue) >= 10) {
+ $this->processBatch();
+ }
+ }
+
+ private function processBatch(): void
+ {
+ // Process events in batches for better performance
+ $notifications = [];
+
+ foreach ($this->eventQueue as $event) {
+ if ($event instanceof SessionCreatedEvent) {
+ $notifications[] = $this->createSessionNotification($event);
+ }
+ }
+
+ // Send all notifications at once
+ $this->notificationService->sendBatch($notifications);
+
+ $this->eventQueue = [];
+ }
+}
+```
+
+## Best practices
+
+### Event handling best practices
+
+- **Keep listeners lightweight**: Avoid heavy operations in event listeners
+- **Use appropriate priority**: Set listener priority based on importance
+- **Handle exceptions**: Prevent one listener from breaking others
+- **Use async processing**: Move heavy operations to message queues
+- **Log important events**: Maintain audit trail for collaboration activities
+
+### Security considerations
+
+- **Validate event data**: Don't trust event data without validation
+- **Check permissions**: Verify user permissions in event listeners
+- **Sanitize input**: Clean any user-provided data in events
+- **Rate limiting**: Implement rate limiting for event-triggered actions
+- **Audit logging**: Log security-relevant collaboration events
+
+## Next steps
+
+- [Explore the PHP API](collaborative_editing_api.md)
+- [View event reference documentation](../../api/event_reference/collaboration_events.md)
\ No newline at end of file
diff --git a/docs/content_management/collaborative_editing/collaborative_editing_guide.md b/docs/content_management/collaborative_editing/collaborative_editing_guide.md
new file mode 100644
index 0000000000..74ecced69b
--- /dev/null
+++ b/docs/content_management/collaborative_editing/collaborative_editing_guide.md
@@ -0,0 +1,123 @@
+---
+description: Enable multiple users to collaborate on content creation, editing, and review through shared drafts, real-time editing, and invitation systems.
+edition: experience,commerce
+month_change: true
+---
+
+# Collaborative editing
+
+Collaborative editing allows multiple users to work together on content creation and editing in [[= product_name =]].
+This feature enables teams to streamline content workflows by sharing drafts, collaborating in real-time, and managing review processes efficiently.
+
+## Key features
+
+### Shared drafts
+
+Content creators can share their draft content with internal team members or external collaborators.
+Shared drafts maintain version control while allowing multiple users to contribute to the same content item.
+
+Key benefits:
+
+- **Version control**: All changes are tracked and versioned
+- **Access control**: Fine-grained permissions for different user types
+- **Dashboard integration**: Shared drafts appear in dedicated dashboard tabs
+- **Workflow integration**: Seamless integration with existing content workflows
+
+### Real-time editing
+
+The real-time editing feature enables users to see each other's changes as they happen, providing immediate visual feedback and preventing conflicts.
+
+Features include:
+
+- **Live cursors**: See where other users are editing
+- **Instant synchronization**: Changes appear immediately for all participants
+- **Conflict prevention**: Smart conflict resolution when multiple users edit the same area
+- **Asynchronous support**: Users can also work offline and sync changes later
+
+### Invitation system
+
+Collaborate with both internal users and external stakeholders through a flexible invitation system.
+
+Invitation options:
+
+- **Internal invitations**: Invite existing [[= product_name =]] users
+- **External invitations**: Invite users outside your organization via email
+- **Time-limited access**: Set expiration dates for collaboration sessions
+- **Role-based permissions**: Control what invited users can do (view, edit, comment)
+
+## Dashboard integration
+
+Collaborative editing extends the back office dashboard with two new tabs:
+
+- **My shared drafts**: Content you've shared with others
+- **Drafts shared with me**: Content others have shared with you
+
+These tabs provide quick access to active collaboration sessions and help users stay organized.
+
+## Getting started
+
+To start using collaborative editing:
+
+1. [Install and configure](collaborative_editing_installation.md) the collaboration feature
+2. [Set up user permissions](collaborative_editing_permissions.md) for collaboration
+3. [Create your first collaboration session](collaborative_editing_usage.md)
+4. Explore the [PHP API](collaborative_editing_api.md) for custom integrations
+
+## Use cases
+
+### Content review process
+
+**Scenario**: Marketing team creates a blog post that needs review from legal and technical teams.
+
+**Solution**:
+1. Marketing creates the initial draft
+2. Share the draft with legal and technical reviewers
+3. Reviewers can comment and suggest changes in real-time
+4. Marketing incorporates feedback and publishes
+
+### External contributor workflow
+
+**Scenario**: Work with external consultants on product documentation.
+
+**Solution**:
+1. Create content structure internally
+2. Invite external experts via email with time-limited access
+3. Collaborate on content creation with real-time feedback
+4. Review and publish internally after collaboration ends
+
+### Multi-language content coordination
+
+**Scenario**: Coordinate content creation across different language teams.
+
+**Solution**:
+1. Create master content in primary language
+2. Share drafts with regional language teams
+3. Parallel translation and localization work
+4. Synchronized publication across all languages
+
+## Technical overview
+
+The collaborative editing system is built on several core components:
+
+- **Session management**: Handles collaboration sessions and participant management
+- **Real-time synchronization**: WebSocket-based real-time updates
+- **Permission system**: Integration with [[= product_name =]] role and policy system
+- **Notification system**: Email and in-app notifications for collaboration events
+- **REST API**: Full API access for custom integrations
+
+## Security considerations
+
+Collaborative editing includes several security features:
+
+- **Permission inheritance**: Respects existing content permissions
+- **Audit logging**: All collaboration activities are logged
+- **Session expiration**: Automatic cleanup of expired sessions
+- **External user limitations**: Configurable restrictions for external collaborators
+
+## Next steps
+
+- [Installation and setup](collaborative_editing_installation.md)
+- [User permissions configuration](collaborative_editing_permissions.md)
+- [Using collaborative editing](collaborative_editing_usage.md)
+- [PHP API reference](collaborative_editing_api.md)
+- [Events and customization](collaborative_editing_events.md)
\ No newline at end of file
diff --git a/docs/content_management/collaborative_editing/collaborative_editing_installation.md b/docs/content_management/collaborative_editing/collaborative_editing_installation.md
new file mode 100644
index 0000000000..84901e4ed7
--- /dev/null
+++ b/docs/content_management/collaborative_editing/collaborative_editing_installation.md
@@ -0,0 +1,324 @@
+---
+description: Install and configure the collaborative editing feature in Ibexa DXP.
+---
+
+# Collaborative editing installation
+
+The collaborative editing feature is available as part of [[= product_name =]] v5.0+ and requires the `ibexa/collaboration` package.
+
+## Requirements
+
+- [[= product_name =]] v5.0 or higher
+- PHP 8.2 or higher
+- Symfony 6.4+ or 7.0+
+- Database: MySQL 8.0+ or PostgreSQL 12+
+- Redis (recommended for real-time features)
+
+## Installation
+
+### 1. Install the collaboration package
+
+The `ibexa/collaboration` package is included by default in [[= product_name =]] v5.0+ Experience and Commerce editions.
+
+For manual installation:
+
+```bash
+composer require ibexa/collaboration
+```
+
+### 2. Enable the bundle
+
+Add the collaboration bundle to your `config/bundles.php`:
+
+```php
+ ['all' => true],
+];
+```
+
+### 3. Configure the database
+
+Run the database migration to create the necessary tables:
+
+```bash
+php bin/console doctrine:migrations:migrate --configuration=vendor/ibexa/collaboration/src/bundle/Resources/config/migrations.yml
+```
+
+This creates the following tables:
+- `ibexa_collaboration_sessions` - Collaboration session data
+- `ibexa_collaboration_participants` - Session participants
+- `ibexa_collaboration_invitations` - Invitation management
+
+### 4. Configure Redis (optional but recommended)
+
+For real-time editing features, configure Redis:
+
+```yaml
+# config/packages/redis.yaml
+framework:
+ cache:
+ app: cache.adapter.redis
+ default_redis_provider: 'redis://localhost:6379'
+
+ibexa:
+ system:
+ default:
+ collaboration:
+ real_time:
+ enabled: true
+ redis_dsn: 'redis://localhost:6379'
+```
+
+### 5. Configure WebSocket support (for real-time editing)
+
+Install and configure a WebSocket server for real-time features:
+
+```bash
+composer require reactphp/socket ratchet/pawl
+```
+
+Add WebSocket configuration:
+
+```yaml
+# config/packages/collaboration.yaml
+ibexa:
+ system:
+ default:
+ collaboration:
+ websocket:
+ enabled: true
+ host: 'localhost'
+ port: 8080
+ path: '/collaboration'
+```
+
+### 6. Configure email notifications
+
+Set up email templates and sender configuration:
+
+```yaml
+# config/packages/collaboration.yaml
+ibexa:
+ system:
+ default:
+ collaboration:
+ notifications:
+ email:
+ enabled: true
+ from_email: 'noreply@example.com'
+ from_name: 'Your Site Name'
+ templates:
+ invitation: '@IbexaCollaboration/emails/invitation.html.twig'
+ session_started: '@IbexaCollaboration/emails/session_started.html.twig'
+```
+
+## Configuration options
+
+### Basic configuration
+
+```yaml
+# config/packages/collaboration.yaml
+ibexa:
+ system:
+ default:
+ collaboration:
+ enabled: true
+
+ # Session management
+ sessions:
+ max_duration: P1D # 1 day
+ cleanup_interval: PT1H # 1 hour
+ max_participants: 10
+
+ # Invitation settings
+ invitations:
+ expiration_time: P7D # 7 days
+ external_users_enabled: true
+ require_confirmation: true
+
+ # Dashboard integration
+ dashboard:
+ enabled: true
+ items_per_page: 20
+```
+
+### Real-time editing configuration
+
+```yaml
+ibexa:
+ system:
+ default:
+ collaboration:
+ real_time:
+ enabled: true
+ sync_interval: 1000 # milliseconds
+ conflict_resolution: 'last_write_wins'
+ cursor_timeout: 30000 # milliseconds
+```
+
+### Security settings
+
+```yaml
+ibexa:
+ system:
+ default:
+ collaboration:
+ security:
+ # External user restrictions
+ external_users:
+ max_session_duration: PT4H # 4 hours
+ allowed_content_types: ['article', 'blog_post']
+ restricted_fields: ['internal_notes']
+
+ # Session security
+ require_https: true
+ session_token_expiry: PT2H # 2 hours
+```
+
+## Permissions setup
+
+### Grant collaboration permissions
+
+Add the necessary policies to user roles:
+
+```yaml
+# In your role configuration
+policies:
+ - module: collaboration
+ function: create_session
+ - module: collaboration
+ function: participate
+ - module: collaboration
+ function: invite
+ limitations:
+ - identifier: User_Group
+ values: [4, 5] # Restrict to specific user groups
+ - module: collaboration
+ function: manage_sessions
+```
+
+Available collaboration policies:
+- `collaboration/create_session` - Create new collaboration sessions
+- `collaboration/participate` - Participate in sessions
+- `collaboration/invite` - Invite other users
+- `collaboration/manage_sessions` - Manage any collaboration session
+- `collaboration/view_shared` - View shared drafts
+
+### Content-specific permissions
+
+Collaborative editing respects existing content permissions. Users need:
+- `content/read` for the content being collaborated on
+- `content/edit` to make changes (if editing is allowed)
+- `content/publish` to publish collaborative changes
+
+## Testing the installation
+
+### 1. Verify bundle installation
+
+Check that the bundle is properly loaded:
+
+```bash
+php bin/console debug:container ibexa.collaboration
+```
+
+### 2. Test database setup
+
+Verify tables were created:
+
+```sql
+SHOW TABLES LIKE 'ibexa_collaboration_%';
+```
+
+### 3. Check permissions
+
+Log into the back office and verify:
+- New dashboard tabs appear: "My shared drafts" and "Drafts shared with me"
+- Collaboration options appear in content editing interface
+- Invitation options are available
+
+### 4. Test basic functionality
+
+1. Create or edit a content item
+2. Look for "Share" or "Collaborate" buttons
+3. Try inviting another user
+4. Verify email notifications are sent
+
+## Troubleshooting
+
+### Common issues
+
+**Bundle not found error**
+```
+Bundle "IbexaCollaborationBundle" not found
+```
+Solution: Ensure the bundle is properly added to `config/bundles.php`
+
+**Database migration failures**
+```
+Migration failed: Table already exists
+```
+Solution: Check if tables already exist and run migrations individually
+
+**WebSocket connection issues**
+```
+WebSocket connection failed
+```
+Solution: Verify Redis is running and WebSocket server is configured correctly
+
+**Permission denied errors**
+```
+Access denied to collaboration feature
+```
+Solution: Check user roles have the necessary collaboration policies
+
+### Debug commands
+
+Enable debug mode for collaboration:
+
+```bash
+php bin/console debug:config ibexa collaboration
+```
+
+View collaboration-related logs:
+
+```bash
+tail -f var/log/collaboration.log
+```
+
+Test Redis connection:
+
+```bash
+redis-cli ping
+```
+
+## Performance considerations
+
+### Database optimization
+
+Add indexes for better performance:
+
+```sql
+CREATE INDEX idx_collaboration_session_status ON ibexa_collaboration_sessions(status);
+CREATE INDEX idx_collaboration_participant_user ON ibexa_collaboration_participants(user_id);
+```
+
+### Caching configuration
+
+Enable proper caching for collaboration data:
+
+```yaml
+doctrine:
+ orm:
+ result_cache_driver: redis
+ metadata_cache_driver: redis
+ query_cache_driver: redis
+```
+
+## Next steps
+
+- [Configure user permissions](collaborative_editing_permissions.md)
+- [Learn how to use collaborative editing](collaborative_editing_usage.md)
+- [Explore the PHP API](collaborative_editing_api.md)
\ No newline at end of file
diff --git a/docs/content_management/collaborative_editing/collaborative_editing_permissions.md b/docs/content_management/collaborative_editing/collaborative_editing_permissions.md
new file mode 100644
index 0000000000..688a602e9d
--- /dev/null
+++ b/docs/content_management/collaborative_editing/collaborative_editing_permissions.md
@@ -0,0 +1,459 @@
+---
+description: Configure user permissions and access control for collaborative editing features.
+---
+
+# Collaborative editing permissions
+
+Collaborative editing integrates with [[= product_name =]]'s role-based permission system to provide fine-grained access control over collaboration features.
+
+## Overview
+
+Collaborative editing permissions work on two levels:
+
+1. **Feature permissions**: Control who can use collaboration features
+2. **Content permissions**: Respect existing content access controls
+
+All collaboration activities respect the underlying content permissions, ensuring users can only collaborate on content they already have access to.
+
+## Collaboration policies
+
+### Core policies
+
+The collaboration module introduces several new policies:
+
+| Policy | Function | Description |
+|--------|----------|-------------|
+| `collaboration` | `create_session` | Create new collaboration sessions |
+| `collaboration` | `participate` | Join and participate in sessions |
+| `collaboration` | `invite` | Invite other users to sessions |
+| `collaboration` | `manage_sessions` | Manage any collaboration session |
+| `collaboration` | `view_shared` | View shared drafts dashboard |
+| `collaboration` | `real_time_edit` | Use real-time editing features |
+
+### Invitation-specific policies
+
+| Policy | Function | Description |
+|--------|----------|-------------|
+| `collaboration` | `invite_internal` | Invite internal users |
+| `collaboration` | `invite_external` | Invite external users via email |
+| `collaboration` | `invite_groups` | Invite entire user groups |
+
+### Session management policies
+
+| Policy | Function | Description |
+|--------|----------|-------------|
+| `collaboration` | `end_session` | End collaboration sessions |
+| `collaboration` | `modify_permissions` | Change participant permissions |
+| `collaboration` | `extend_session` | Extend session duration |
+| `collaboration` | `export_data` | Export collaboration data |
+
+## Policy configuration examples
+
+### Basic collaboration user
+
+A typical content editor who can participate in collaboration:
+
+```yaml
+# config/packages/security.yaml
+role_editor_collaboration:
+ policies:
+ - module: collaboration
+ function: create_session
+ - module: collaboration
+ function: participate
+ - module: collaboration
+ function: invite_internal
+ - module: collaboration
+ function: view_shared
+ - module: content
+ function: read
+ - module: content
+ function: edit
+```
+
+### Advanced collaboration manager
+
+A user who can manage collaboration sessions for their team:
+
+```yaml
+role_collaboration_manager:
+ policies:
+ - module: collaboration
+ function: manage_sessions
+ limitations:
+ - identifier: User_Group
+ values: [12, 13] # Specific user groups
+ - module: collaboration
+ function: invite_external
+ - module: collaboration
+ function: modify_permissions
+ - module: collaboration
+ function: end_session
+ - module: collaboration
+ function: export_data
+```
+
+### External collaboration user (limited)
+
+Restricted permissions for external collaborators:
+
+```yaml
+role_external_collaborator:
+ policies:
+ - module: collaboration
+ function: participate
+ limitations:
+ - identifier: Content_Type
+ values: ['article', 'blog_post']
+ - module: collaboration
+ function: view_shared
+ - module: content
+ function: read
+ limitations:
+ - identifier: Section
+ values: [2] # Public section only
+```
+
+## Limitations
+
+### User Group limitation
+
+Restrict collaboration to specific user groups:
+
+```yaml
+policies:
+ - module: collaboration
+ function: invite
+ limitations:
+ - identifier: User_Group
+ values: [4, 5, 6] # Marketing, Editorial, Design teams
+```
+
+### Content Type limitation
+
+Limit collaboration to specific content types:
+
+```yaml
+policies:
+ - module: collaboration
+ function: create_session
+ limitations:
+ - identifier: Content_Type
+ values: ['article', 'blog_post', 'landing_page']
+```
+
+### Section limitation
+
+Restrict collaboration to content in specific sections:
+
+```yaml
+policies:
+ - module: collaboration
+ function: participate
+ limitations:
+ - identifier: Section
+ values: [1, 2] # Standard and Media sections
+```
+
+### Time-based limitations
+
+Configure time-based restrictions (configured at system level):
+
+```yaml
+# config/packages/collaboration.yaml
+ibexa:
+ system:
+ default:
+ collaboration:
+ limitations:
+ session_duration:
+ default: P1D # 1 day
+ external_users: PT4H # 4 hours for external users
+ daily_sessions:
+ max_per_user: 5
+ max_per_content: 3
+```
+
+## Permission inheritance
+
+### Content permissions
+
+Collaborative editing respects existing content permissions:
+
+- **Read access**: Users must have `content/read` for the content being collaborated on
+- **Edit access**: To make changes, users need `content/edit` permissions
+- **Publish access**: Only users with `content/publish` can finalize collaborative changes
+
+### Location-based permissions
+
+Content location permissions apply to collaboration:
+
+```yaml
+policies:
+ - module: content
+ function: read
+ limitations:
+ - identifier: Subtree
+ values: ['/1/2/'] # Media subtree
+ - module: collaboration
+ function: participate # Can only collaborate on content in Media subtree
+```
+
+### Language permissions
+
+Multi-language content collaboration respects language limitations:
+
+```yaml
+policies:
+ - module: content
+ function: edit
+ limitations:
+ - identifier: Language
+ values: ['eng-US', 'fre-FR']
+ - module: collaboration
+ function: create_session # Can collaborate on English and French content
+```
+
+## External user permissions
+
+### Configuration
+
+Configure external user access in system settings:
+
+```yaml
+# config/packages/collaboration.yaml
+ibexa:
+ system:
+ default:
+ collaboration:
+ external_users:
+ enabled: true
+ max_session_duration: PT4H
+ allowed_content_types: ['article', 'blog_post']
+ restricted_fields: ['internal_notes', 'seo_metadata']
+ require_terms_acceptance: true
+```
+
+### External user role template
+
+Create a template role for external users:
+
+```yaml
+role_external_template:
+ policies:
+ - module: collaboration
+ function: participate
+ limitations:
+ - identifier: Content_Type
+ values: ['article']
+ - module: content
+ function: read
+ limitations:
+ - identifier: Section
+ values: [2] # Public section only
+```
+
+### Email domain restrictions
+
+Restrict external invitations to specific email domains:
+
+```yaml
+ibexa:
+ system:
+ default:
+ collaboration:
+ external_users:
+ allowed_domains: ['partner.com', 'contractor.org']
+ blocked_domains: ['competitor.com']
+```
+
+## Session-level permissions
+
+### Participant roles
+
+Each collaboration session can have different participant roles:
+
+| Role | Permissions | Use Case |
+|------|-------------|----------|
+| **Owner** | Full control over session | Session creator |
+| **Admin** | Manage participants, end session | Team leads |
+| **Editor** | Edit content, comment | Content creators |
+| **Reviewer** | View, comment, approve/reject | Reviewers |
+| **Viewer** | View only, comment | Stakeholders |
+
+### Dynamic permission assignment
+
+Assign permissions when inviting users:
+
+```php
+// In your invitation code
+$invitation = $collaborationService->createInvitation($session, [
+ 'user_id' => $userId,
+ 'role' => 'editor',
+ 'permissions' => [
+ 'edit' => true,
+ 'comment' => true,
+ 'invite_others' => false,
+ 'end_session' => false,
+ ],
+ 'expiration' => new \DateTime('+7 days'),
+]);
+```
+
+## Permission checking examples
+
+### Check if user can create sessions
+
+```php
+use Ibexa\Contracts\Core\Repository\PermissionResolver;
+
+class CollaborationPermissionChecker
+{
+ public function canCreateSession(User $user, Content $content): bool
+ {
+ return $this->permissionResolver->hasAccess('collaboration', 'create_session') &&
+ $this->permissionResolver->canUser('content', 'edit', $content);
+ }
+}
+```
+
+### Check collaboration permissions in templates
+
+```twig
+{% if ibexa_is_granted('collaboration', 'create_session') %}
+
+{% endif %}
+
+{% if ibexa_is_granted('collaboration', 'invite_external') %}
+
+{% endif %}
+```
+
+### Verify session access
+
+```php
+public function canAccessSession(User $user, CollaborationSession $session): bool
+{
+ // Check if user is participant
+ if ($session->isParticipant($user)) {
+ return true;
+ }
+
+ // Check if user has manage_sessions permission
+ if ($this->permissionResolver->hasAccess('collaboration', 'manage_sessions')) {
+ return true;
+ }
+
+ // Check if user has access to the content
+ return $this->permissionResolver->canUser('content', 'read', $session->getContent());
+}
+```
+
+## Security considerations
+
+### Session security
+
+- **Token-based access**: Each session uses unique tokens for participant authentication
+- **IP restrictions**: Optionally restrict sessions to specific IP addresses
+- **Device limits**: Limit number of concurrent devices per user
+- **Audit logging**: All collaboration activities are logged for security review
+
+### Data protection
+
+- **Content isolation**: Users can only access content they have permissions for
+- **Change attribution**: All modifications are attributed to specific users
+- **Export restrictions**: Control who can export collaboration data
+- **Cleanup policies**: Automatic cleanup of expired sessions and data
+
+### External user security
+
+- **Limited access**: External users have restricted permissions by default
+- **Time limits**: Enforce shorter session durations for external users
+- **Content restrictions**: Limit which content types external users can access
+- **Email verification**: Require email verification for external participants
+
+## Troubleshooting permissions
+
+### Common permission issues
+
+**Cannot start collaboration**
+```
+Check: collaboration/create_session policy
+Check: content/edit permission on target content
+Check: User is not exceeding session limits
+```
+
+**Cannot invite users**
+```
+Check: collaboration/invite_internal or collaboration/invite_external policy
+Check: Target users have necessary content permissions
+Check: Email domain restrictions for external users
+```
+
+**Session access denied**
+```
+Check: Session is still active (not expired)
+Check: User is valid participant or has manage_sessions policy
+Check: Content permissions still valid
+```
+
+### Debug permission issues
+
+Use the debug commands to troubleshoot:
+
+```bash
+# Check user permissions
+php bin/console debug:permission collaboration create_session --user=john
+
+# List all collaboration policies
+php bin/console debug:permission --module=collaboration
+
+# Check content permissions
+php bin/console debug:permission content edit --content-id=123
+```
+
+### Enable permission logging
+
+Add detailed logging for permission checks:
+
+```yaml
+# config/packages/monolog.yaml
+monolog:
+ handlers:
+ collaboration_permissions:
+ type: stream
+ path: '%kernel.logs_dir%/collaboration_permissions.log'
+ channels: ['collaboration_permissions']
+ level: debug
+```
+
+## Best practices
+
+### Role design
+
+- **Principle of least privilege**: Grant minimum necessary permissions
+- **Clear role hierarchy**: Define clear relationships between roles
+- **Regular review**: Periodically review and update permission assignments
+- **Documentation**: Document custom roles and their intended use cases
+
+### External user management
+
+- **Time limits**: Use shorter session durations for external users
+- **Content restrictions**: Limit external access to public or shared content
+- **Review requirements**: Require internal review of external user contributions
+- **Terms and conditions**: Require acceptance of collaboration terms
+
+### Monitoring and auditing
+
+- **Regular audits**: Review collaboration activity logs regularly
+- **Permission changes**: Monitor changes to collaboration policies
+- **Session analytics**: Track session usage and participation patterns
+- **Security alerts**: Set up alerts for suspicious collaboration activity
+
+## Next steps
+
+- [Learn how to use collaborative editing](collaborative_editing_usage.md)
+- [Explore the PHP API](collaborative_editing_api.md)
+- [Set up custom events and notifications](collaborative_editing_events.md)
\ No newline at end of file
diff --git a/docs/content_management/collaborative_editing/collaborative_editing_usage.md b/docs/content_management/collaborative_editing/collaborative_editing_usage.md
new file mode 100644
index 0000000000..bb674ab5a1
--- /dev/null
+++ b/docs/content_management/collaborative_editing/collaborative_editing_usage.md
@@ -0,0 +1,266 @@
+---
+description: Learn how to use collaborative editing to work with team members on content creation and editing.
+---
+
+# Using collaborative editing
+
+This guide walks you through the collaborative editing features, from creating your first collaboration session to managing shared drafts and working with real-time editing.
+
+## Starting a collaboration session
+
+### From content editing
+
+1. **Open content for editing**: Navigate to any content item and click "Edit"
+2. **Start collaboration**: Look for the "Collaborate" or "Share" button in the toolbar
+3. **Configure session**:
+ - Set session name (optional)
+ - Choose collaboration type (view-only, edit, or full access)
+ - Set expiration date
+4. **Invite participants**: Add users by username, email, or user group
+
+
+
+### From draft management
+
+1. **Navigate to drafts**: Go to your dashboard and select "My drafts"
+2. **Share existing draft**: Click the share icon next to any draft
+3. **Configure sharing options**: Set permissions and expiration
+4. **Send invitations**: Choose recipients and send invitations
+
+## Managing collaboration sessions
+
+### Session dashboard
+
+Access your collaboration sessions through the dedicated dashboard tabs:
+
+- **My shared drafts**: Content you've shared with others
+- **Drafts shared with me**: Content others have shared with you
+
+
+
+The dashboard shows:
+- Session status (active, expired, completed)
+- Number of participants
+- Last activity timestamp
+- Quick actions (edit, manage, end session)
+
+### Session details
+
+Click on any session to view detailed information:
+
+- **Participants list**: Who's currently in the session
+- **Activity log**: Recent changes and comments
+- **Session settings**: Permissions and expiration details
+- **Actions**: End session, modify permissions, export changes
+
+## Working with invitations
+
+### Sending invitations
+
+When starting a collaboration session, you can invite:
+
+**Internal users**:
+- Search by username or display name
+- Select from user group membership
+- Assign specific roles (viewer, editor, reviewer)
+
+**External users**:
+- Enter email addresses
+- Set limited access permissions
+- Configure session duration (typically shorter than internal users)
+
+
+
+### Receiving invitations
+
+When someone invites you to collaborate:
+
+1. **Email notification**: You'll receive an invitation email with session details
+2. **Dashboard notification**: New sessions appear in "Drafts shared with me"
+3. **Join session**: Click the invitation link or join from the dashboard
+4. **Accept terms**: Review and accept collaboration terms (if configured)
+
+### Managing received invitations
+
+From the "Drafts shared with me" tab:
+
+- **Join active sessions**: Click "Join" to enter collaboration mode
+- **View session info**: See who else is participating and session details
+- **Leave sessions**: Exit collaboration if no longer needed
+- **Notifications**: Configure how you want to be notified of session activity
+
+## Real-time collaboration features
+
+### Live editing
+
+When real-time editing is enabled, you'll see:
+
+**User cursors**: Live cursors showing where other users are currently editing
+
+**Instant updates**: Changes appear immediately as other users type
+
+**Conflict indicators**: Visual warnings when multiple users edit the same area
+
+**User presence**: List of currently active users in the session
+
+
+
+### Working asynchronously
+
+Even without real-time editing, you can collaborate effectively:
+
+**Change tracking**: All modifications are tracked with user attribution
+
+**Comment system**: Leave comments on specific content sections
+
+**Version comparison**: See what changed between your last visit
+
+**Merge assistance**: Guided merge process when conflicts occur
+
+## Collaboration workflow examples
+
+### Content review workflow
+
+**Scenario**: Blog post requiring legal and technical review
+
+1. **Author creates draft**: Marketing team writes initial blog post
+2. **Share for review**: Author shares draft with legal and technical teams as "reviewers"
+3. **Parallel review**: Both teams can review simultaneously, leaving comments
+4. **Author incorporates feedback**: Marketing team addresses comments and updates content
+5. **Final approval**: Reviewers confirm changes and approve for publication
+6. **Publish**: Author publishes the final version
+
+### External collaboration
+
+**Scenario**: Working with external consultants on documentation
+
+1. **Create collaboration session**: Internal team creates draft and shares with consultants
+2. **Time-limited access**: External users get 4-hour access windows
+3. **Guided collaboration**: Internal users facilitate sessions and provide context
+4. **Knowledge transfer**: External experts contribute specialized content
+5. **Internal review**: Internal team reviews and finalizes content before publication
+
+### Multi-team content creation
+
+**Scenario**: Product announcement requiring input from multiple departments
+
+1. **Initial structure**: Product marketing creates content outline
+2. **Parallel contribution**: Technical writers, designers, and developers contribute simultaneously
+3. **Real-time coordination**: Teams see each other's work and coordinate in real-time
+4. **Review and approval**: Management reviews consolidated content
+5. **Launch coordination**: All teams finalize their sections before coordinated publication
+
+## Comments and feedback
+
+### Leaving comments
+
+Add comments to specific content sections:
+
+1. **Select text**: Highlight the content you want to comment on
+2. **Add comment**: Click the comment button or use keyboard shortcut
+3. **Write feedback**: Provide constructive feedback or ask questions
+4. **Tag participants**: Use @mentions to notify specific users
+5. **Submit**: Comment is immediately visible to all participants
+
+### Managing comments
+
+**Reply to comments**: Continue the conversation by replying to existing comments
+
+**Resolve comments**: Mark comments as resolved once addressed
+
+**Filter comments**: View only your comments, unresolved issues, or all feedback
+
+**Export comments**: Download comment summary for external review
+
+
+
+## Session management
+
+### Ending sessions
+
+**Manual termination**: End active sessions when collaboration is complete
+
+**Automatic expiration**: Sessions end automatically based on configured timeouts
+
+**Graceful shutdown**: All participants receive notifications before session ends
+
+**Data preservation**: Comments and change history are preserved after session ends
+
+### Permissions during collaboration
+
+Participants can have different permission levels:
+
+**Viewer**: Can see content and leave comments but cannot edit
+
+**Editor**: Can modify content and participate fully in collaboration
+
+**Reviewer**: Can see changes, comment, and approve/reject modifications
+
+**Administrator**: Full control over session, including ending and managing participants
+
+### Monitoring activity
+
+Track collaboration progress through:
+
+**Activity feeds**: Real-time updates on participant actions
+
+**Change summaries**: Consolidated view of all modifications made during session
+
+**Participation metrics**: See who's contributing most actively
+
+**Time tracking**: Monitor how long users spend in sessions
+
+## Best practices
+
+### Before starting collaboration
+
+- **Define objectives**: Clearly communicate what you want to achieve
+- **Set expectations**: Let participants know their roles and responsibilities
+- **Prepare content**: Have a solid foundation before inviting others
+- **Check permissions**: Ensure all participants have necessary access rights
+
+### During collaboration
+
+- **Stay focused**: Keep discussions relevant to the content being developed
+- **Use comments effectively**: Provide specific, actionable feedback
+- **Communicate changes**: Explain significant modifications you're making
+- **Respect others' work**: Don't overwrite others' contributions without discussion
+
+### After collaboration
+
+- **Review changes**: Check all modifications before finalizing content
+- **Archive sessions**: Properly close sessions when work is complete
+- **Document decisions**: Preserve important discussion points and decisions
+- **Follow up**: Ensure all feedback has been addressed
+
+## Troubleshooting common issues
+
+### Connection problems
+
+**Real-time sync issues**: Refresh the page or rejoin the session
+
+**Missing updates**: Check your internet connection and session status
+
+**Performance slowdown**: Reduce number of active participants or use asynchronous mode
+
+### Permission problems
+
+**Cannot edit content**: Verify you have edit permissions for the underlying content
+
+**Cannot invite users**: Check if you have collaboration/invite policy permissions
+
+**Access denied errors**: Ensure your user role includes necessary collaboration policies
+
+### Session management issues
+
+**Session expired**: Check expiration settings and renew if necessary
+
+**Cannot join session**: Verify invitation is still valid and you're logged in
+
+**Missing shared drafts**: Check if content has been moved or permissions changed
+
+## Next steps
+
+- [Configure advanced permissions](collaborative_editing_permissions.md)
+- [Explore the PHP API](collaborative_editing_api.md)
+- [Set up custom notifications](collaborative_editing_events.md)
\ No newline at end of file
diff --git a/docs/content_management/collaborative_editing/img/collaboration_comments.png b/docs/content_management/collaborative_editing/img/collaboration_comments.png
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/content_management/collaborative_editing/img/collaboration_comments.png.txt b/docs/content_management/collaborative_editing/img/collaboration_comments.png.txt
new file mode 100644
index 0000000000..f5a4d522bd
--- /dev/null
+++ b/docs/content_management/collaborative_editing/img/collaboration_comments.png.txt
@@ -0,0 +1 @@
+Placeholder for collaboration_comments.png
diff --git a/docs/content_management/collaborative_editing/img/collaboration_dashboard.png b/docs/content_management/collaborative_editing/img/collaboration_dashboard.png
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/content_management/collaborative_editing/img/collaboration_dashboard.png.txt b/docs/content_management/collaborative_editing/img/collaboration_dashboard.png.txt
new file mode 100644
index 0000000000..46fd678084
--- /dev/null
+++ b/docs/content_management/collaborative_editing/img/collaboration_dashboard.png.txt
@@ -0,0 +1 @@
+Placeholder for collaboration_dashboard.png
diff --git a/docs/content_management/collaborative_editing/img/realtime_editing.png b/docs/content_management/collaborative_editing/img/realtime_editing.png
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/content_management/collaborative_editing/img/realtime_editing.png.txt b/docs/content_management/collaborative_editing/img/realtime_editing.png.txt
new file mode 100644
index 0000000000..1cc2fd1005
--- /dev/null
+++ b/docs/content_management/collaborative_editing/img/realtime_editing.png.txt
@@ -0,0 +1 @@
+Placeholder for realtime_editing.png
diff --git a/docs/content_management/collaborative_editing/img/send_invitations.png b/docs/content_management/collaborative_editing/img/send_invitations.png
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/content_management/collaborative_editing/img/send_invitations.png.txt b/docs/content_management/collaborative_editing/img/send_invitations.png.txt
new file mode 100644
index 0000000000..d22f995d52
--- /dev/null
+++ b/docs/content_management/collaborative_editing/img/send_invitations.png.txt
@@ -0,0 +1 @@
+Placeholder for send_invitations.png
diff --git a/docs/content_management/collaborative_editing/img/start_collaboration.png b/docs/content_management/collaborative_editing/img/start_collaboration.png
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/content_management/collaborative_editing/img/start_collaboration.png.txt b/docs/content_management/collaborative_editing/img/start_collaboration.png.txt
new file mode 100644
index 0000000000..925031bd03
--- /dev/null
+++ b/docs/content_management/collaborative_editing/img/start_collaboration.png.txt
@@ -0,0 +1 @@
+Placeholder for start_collaboration.png
diff --git a/docs/content_management/content_management.md b/docs/content_management/content_management.md
index d83dda3271..d2849dbad7 100644
--- a/docs/content_management/content_management.md
+++ b/docs/content_management/content_management.md
@@ -14,5 +14,6 @@ page_type: landing_page
"content_management/forms/forms",
"content_management/taxonomy/taxonomy",
"content_management/workflow/workflow",
+ "content_management/collaborative_editing/collaborative_editing_guide",
"content_management/data_migration/data_migration",
], columns=4) =]]
diff --git a/mkdocs.yml b/mkdocs.yml
index ca209a9202..26df3fd747 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -101,6 +101,7 @@ nav:
- Trash events: api/event_reference/trash_events.md
- Twig Components: api/event_reference/twig_component_events.md
- AI Action events: api/event_reference/ai_action_events.md
+ - Collaboration events: api/event_reference/collaboration_events.md
- Discounts events: api/event_reference/discounts_events.md
- Other events: api/event_reference/other_events.md
- Administration:
@@ -280,7 +281,12 @@ nav:
- URL field type: content_management/field_types/field_type_reference/urlfield.md
- User field type: content_management/field_types/field_type_reference/userfield.md
- Collaborative editing:
- - Collaborative editing product guide: content_management/collaborative_editing/collaborative_editing_guide.md
+ - Collaborative editing guide: content_management/collaborative_editing/collaborative_editing_guide.md
+ - Installation: content_management/collaborative_editing/collaborative_editing_installation.md
+ - Usage: content_management/collaborative_editing/collaborative_editing_usage.md
+ - Permissions: content_management/collaborative_editing/collaborative_editing_permissions.md
+ - PHP API: content_management/collaborative_editing/collaborative_editing_api.md
+ - Events and customization: content_management/collaborative_editing/collaborative_editing_events.md
- Templating:
- Templating: templating/templating.md
- Render content: