diff --git a/code_samples/collaboration/config/mysql/ibexa_share.sql b/code_samples/collaboration/config/mysql/ibexa_share.sql new file mode 100644 index 0000000000..7bf19643af --- /dev/null +++ b/code_samples/collaboration/config/mysql/ibexa_share.sql @@ -0,0 +1,82 @@ +CREATE TABLE ibexa_collaboration +( + id INT AUTO_INCREMENT NOT NULL, + owner_id INT NOT NULL, + token VARCHAR(160) NOT NULL, + discriminator VARCHAR(190) NOT NULL, + is_active TINYINT(1) NOT NULL, + has_public_link TINYINT(1) NOT NULL, + created_at DATETIME NOT NULL COMMENT '(DC2Type:datetimetz_immutable)', + updated_at DATETIME NOT NULL COMMENT '(DC2Type:datetimetz_immutable)', + UNIQUE INDEX ibexa_collaboration_token_idx (token), + INDEX ibexa_collaboration_owner_idx (owner_id), + UNIQUE INDEX ibexa_collaboration_token_uc (token), + PRIMARY KEY (id) +) DEFAULT CHARACTER SET utf8mb4 + COLLATE `utf8mb4_unicode_520_ci` + ENGINE = InnoDB; +CREATE TABLE ibexa_collaboration_participant +( + id INT AUTO_INCREMENT NOT NULL, + session_id INT NOT NULL, + discriminator VARCHAR(190) NOT NULL, + scope VARCHAR(255) DEFAULT NULL, + token VARCHAR(255) DEFAULT NULL, + created_at DATETIME NOT NULL COMMENT '(DC2Type:datetimetz_immutable)', + updated_at DATETIME NOT NULL COMMENT '(DC2Type:datetimetz_immutable)', + INDEX IDX_9C5C6401613FECDF (session_id), + UNIQUE INDEX ibexa_collaboration_participant_token_idx (token), + PRIMARY KEY (id) +) DEFAULT CHARACTER SET utf8mb4 + COLLATE `utf8mb4_unicode_520_ci` + ENGINE = InnoDB; +CREATE TABLE ibexa_collaboration_participant_internal +( + id INT NOT NULL, + user_id INT NOT NULL, + INDEX IDX_E838B79AA76ED395 (user_id), + PRIMARY KEY (id) +) DEFAULT CHARACTER SET utf8mb4 + COLLATE `utf8mb4_unicode_520_ci` + ENGINE = InnoDB; +CREATE TABLE ibexa_collaboration_participant_external +( + id INT NOT NULL, + email VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +) DEFAULT CHARACTER SET utf8mb4 + COLLATE `utf8mb4_unicode_520_ci` + ENGINE = InnoDB; +CREATE TABLE ibexa_collaboration_invitation +( + id INT AUTO_INCREMENT NOT NULL, + session_id INT NOT NULL, + participant_id INT NOT NULL, + sender_id INT NOT NULL, + status VARCHAR(64) NOT NULL, + context LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json)', + created_at DATETIME NOT NULL COMMENT '(DC2Type:datetimetz_immutable)', + updated_at DATETIME NOT NULL COMMENT '(DC2Type:datetimetz_immutable)', + INDEX IDX_36C63687613FECDF (session_id), + INDEX IDX_36C636879D1C3019 (participant_id), + INDEX IDX_36C63687F624B39D (sender_id), + PRIMARY KEY (id) +) DEFAULT CHARACTER SET utf8mb4 + COLLATE `utf8mb4_unicode_520_ci` + ENGINE = InnoDB; +ALTER TABLE ibexa_collaboration + ADD CONSTRAINT ibexa_collaboration_owner_id_fk FOREIGN KEY (owner_id) REFERENCES ezuser (contentobject_id) ON DELETE RESTRICT; +ALTER TABLE ibexa_collaboration_participant + ADD CONSTRAINT ibexa_collaboration_participant_session_id_fk FOREIGN KEY (session_id) REFERENCES ibexa_collaboration (id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ibexa_collaboration_participant_internal + ADD CONSTRAINT ibexa_collaboration_participant_internal_pk FOREIGN KEY (id) REFERENCES ibexa_collaboration_participant (id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ibexa_collaboration_participant_internal + ADD CONSTRAINT ibexa_collaboration_participant_internal_user_id_fk FOREIGN KEY (user_id) REFERENCES ezuser (contentobject_id) ON DELETE RESTRICT; +ALTER TABLE ibexa_collaboration_participant_external + ADD CONSTRAINT ibexa_collaboration_participant_external_pk FOREIGN KEY (id) REFERENCES ibexa_collaboration_participant (id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ibexa_collaboration_invitation + ADD CONSTRAINT ibexa_collaboration_invitation_session_id_fk FOREIGN KEY (session_id) REFERENCES ibexa_collaboration (id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ibexa_collaboration_invitation + ADD CONSTRAINT ibexa_collaboration_invitation_participant_id_fk FOREIGN KEY (participant_id) REFERENCES ibexa_collaboration_participant (id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ibexa_collaboration_invitation + ADD CONSTRAINT ibexa_collaboration_invitation_sender_id_fk FOREIGN KEY (sender_id) REFERENCES ezuser (contentobject_id) ON DELETE RESTRICT; diff --git a/code_samples/collaboration/config/postgresql/ibexa_share.sql b/code_samples/collaboration/config/postgresql/ibexa_share.sql new file mode 100644 index 0000000000..fc860430c8 --- /dev/null +++ b/code_samples/collaboration/config/postgresql/ibexa_share.sql @@ -0,0 +1,84 @@ +CREATE TABLE ibexa_collaboration +( + id SERIAL NOT NULL, + owner_id INT NOT NULL, + token VARCHAR(160) NOT NULL, + discriminator VARCHAR(190) NOT NULL, + is_active BOOLEAN NOT NULL, + has_public_link BOOLEAN NOT NULL, + created_at TIMESTAMP(0) WITH TIME ZONE NOT NULL, + updated_at TIMESTAMP(0) WITH TIME ZONE NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX ibexa_collaboration_token_idx ON ibexa_collaboration (token); +CREATE INDEX ibexa_collaboration_owner_idx ON ibexa_collaboration (owner_id); +CREATE UNIQUE INDEX ibexa_collaboration_token_uc ON ibexa_collaboration (token); +COMMENT +ON COLUMN ibexa_collaboration.created_at IS '(DC2Type:datetimetz_immutable)'; +COMMENT +ON COLUMN ibexa_collaboration.updated_at IS '(DC2Type:datetimetz_immutable)'; +CREATE TABLE ibexa_collaboration_participant +( + id SERIAL NOT NULL, + session_id INT NOT NULL, + discriminator VARCHAR(190) NOT NULL, + scope VARCHAR(255) DEFAULT NULL, + token VARCHAR(255) DEFAULT NULL, + created_at TIMESTAMP(0) WITH TIME ZONE NOT NULL, + updated_at TIMESTAMP(0) WITH TIME ZONE NOT NULL, + PRIMARY KEY (id) +); +CREATE INDEX ibexa_collaboration_participant_idx ON ibexa_collaboration_participant (session_id); +CREATE UNIQUE INDEX ibexa_collaboration_participant_token_idx ON ibexa_collaboration_participant (token); +COMMENT +ON COLUMN ibexa_collaboration_participant.created_at IS '(DC2Type:datetimetz_immutable)'; +COMMENT +ON COLUMN ibexa_collaboration_participant.updated_at IS '(DC2Type:datetimetz_immutable)'; +CREATE TABLE ibexa_collaboration_participant_internal +( + id INT NOT NULL, + user_id INT NOT NULL, + PRIMARY KEY (id) +); +CREATE INDEX ibexa_collaboration_participant_internal_idx ON ibexa_collaboration_participant_internal (user_id); +CREATE TABLE ibexa_collaboration_participant_external +( + id INT NOT NULL, + email VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +); +CREATE TABLE ibexa_collaboration_invitation +( + id SERIAL NOT NULL, + session_id INT NOT NULL, + participant_id INT NOT NULL, + sender_id INT NOT NULL, + status VARCHAR(64) NOT NULL, + context JSON DEFAULT NULL, + created_at TIMESTAMP(0) WITH TIME ZONE NOT NULL, + updated_at TIMESTAMP(0) WITH TIME ZONE NOT NULL, + PRIMARY KEY (id) +); +CREATE INDEX ibexa_collaboration_invitation_idx ON ibexa_collaboration_invitation (session_id); +CREATE INDEX ibexa_collaboration_invitation_idx ON ibexa_collaboration_invitation (participant_id); +CREATE INDEX ibexa_collaboration_invitation_idx ON ibexa_collaboration_invitation (sender_id); +COMMENT +ON COLUMN ibexa_collaboration_invitation.created_at IS '(DC2Type:datetimetz_immutable)'; +COMMENT +ON COLUMN ibexa_collaboration_invitation.updated_at IS '(DC2Type:datetimetz_immutable)'; +ALTER TABLE ibexa_collaboration + ADD CONSTRAINT ibexa_collaboration_owner_id_fk FOREIGN KEY (owner_id) REFERENCES ezuser (contentobject_id) ON DELETE RESTRICT NOT DEFERRABLE INITIALLY IMMEDIATE; +ALTER TABLE ibexa_collaboration_participant + ADD CONSTRAINT ibexa_collaboration_participant_session_id_fk FOREIGN KEY (session_id) REFERENCES ibexa_collaboration (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE; +ALTER TABLE ibexa_collaboration_participant_internal + ADD CONSTRAINT ibexa_collaboration_participant_internal_pk FOREIGN KEY (id) REFERENCES ibexa_collaboration_participant (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE; +ALTER TABLE ibexa_collaboration_participant_internal + ADD CONSTRAINT ibexa_collaboration_participant_internal_user_id_fk FOREIGN KEY (user_id) REFERENCES ezuser (contentobject_id) ON DELETE RESTRICT NOT DEFERRABLE INITIALLY IMMEDIATE; +ALTER TABLE ibexa_collaboration_participant_external + ADD CONSTRAINT ibexa_collaboration_participant_external_pk FOREIGN KEY (id) REFERENCES ibexa_collaboration_participant (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE; +ALTER TABLE ibexa_collaboration_invitation + ADD CONSTRAINT ibexa_collaboration_invitation_session_id_fk FOREIGN KEY (session_id) REFERENCES ibexa_collaboration (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE; +ALTER TABLE ibexa_collaboration_invitation + ADD CONSTRAINT ibexa_collaboration_invitation_participant_id_fk FOREIGN KEY (participant_id) REFERENCES ibexa_collaboration_participant (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE; +ALTER TABLE ibexa_collaboration_invitation + ADD CONSTRAINT ibexa_collaboration_invitation_sender_id_fk FOREIGN KEY (sender_id) REFERENCES ezuser (contentobject_id) ON DELETE RESTRICT NOT DEFERRABLE INITIALLY IMMEDIATE; \ No newline at end of file diff --git a/code_samples/collaboration/src/Command/ManageSessionsCommand.php b/code_samples/collaboration/src/Command/ManageSessionsCommand.php new file mode 100644 index 0000000000..d6fbe6b3a8 --- /dev/null +++ b/code_samples/collaboration/src/Command/ManageSessionsCommand.php @@ -0,0 +1,174 @@ +invitationService = $invitationService; + $this->sessionService = $sessionService; + $this->contentService = $contentService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + $this->permissionResolver->setCurrentUserReference( + $this->userService->loadUserByLogin('admin') + ); + + // Create a sharing session for Content + $versionInfo = $this->contentService->loadContent(52)->getVersionInfo(); + $createStruct = new ContentSessionCreateStruct( + $versionInfo, + $versionInfo->getInitialLanguage() + ); + $createStruct->setHasPublicLink(false); + + $token = 'my-secret-token-12345'; + $createStruct->setToken($token); + + $sessionId = $this->sessionService->createSession($createStruct)->getId(); + + // Get a session by ID or token + $session = $this->sessionService->getSession($sessionId); + $session = $this->sessionService->getSessionByToken($token); + + // Find sessions + $sessionQuery = new SessionQuery(new Token($token)); + $session = $this->sessionService->findSessions($sessionQuery)->getFirst(); + + // Update a session + $updateStruct = new ContentSessionUpdateStruct(); + $updateStruct->setHasPublicLink(true); + + $this->sessionService->updateSession($session, $updateStruct); + + // Deactivate a session + $updateStruct = new ContentSessionUpdateStruct(); + $updateStruct->setIsActive(false); + + $this->sessionService->updateSession($session, $updateStruct); + + // Manage participants + $user = $this->userService->loadUserByLogin('another_user'); + $internalParticipantCreateStruct = new InternalParticipantCreateStruct( + $user, + ContentSessionScope::VIEW + ); + $externalParticipantCreateStruct = new ExternalParticipantCreateStruct( + 'external@example.com', + ContentSessionScope::VIEW, + 'personal-secret-token-12345' + ); + + $internalParticipant = $this->sessionService->addParticipant($session, $internalParticipantCreateStruct); + $externalParticipant = $this->sessionService->addParticipant($session, $externalParticipantCreateStruct); + + // Get and update participants + $participant = $this->sessionService + ->getSession($session->getId()) + ->getParticipants() + ->getByEmail($user->email); + + $internalParticipantUpdateStruct = new InternalParticipantUpdateStruct(ContentSessionScope::EDIT); + $this->sessionService->updateParticipant($session, $participant, $internalParticipantUpdateStruct); + + // Remove participant + $this->sessionService->removeParticipant($session, $externalParticipant); + + // Check ownerships. If no user is provided, current user is used. + $this->sessionService->isSessionOwner( + $session, + $this->userService->loadUserByLogin('another_user') + ); + + // Check participation + $this->sessionService->isSessionParticipant( + $session, + $this->permissionResolver->getCurrentUserReference() + ); + + // Manage invitations + $invitationQuery = new InvitationQuery(new Session($session)); + $invitations = $this->invitationService->findInvitations($invitationQuery)->getInvitations(); + + foreach ($invitations as $invitation) { + $output->writeln('Invitation ID: ' . $invitation->getId() . ' Status: ' . $invitation->getStatus()); + } + + $invitation = $this->invitationService->getInvitationByParticipant($participant); + + // Create invitation - use when auto-inviting participants is not enabled + $invitationCreateStruct = new InvitationCreateStruct( + $session, + $internalParticipant + ); + + $this->invitationService->createInvitation($invitationCreateStruct); + + // Update invitation + $invitationUpdateStruct = new InvitationUpdateStruct(); + $invitationUpdateStruct->setStatus(InvitationStatus::STATUS_REJECTED); + + $this->invitationService->updateInvitation($invitation, $invitationUpdateStruct); + + // Delete invitation + $invitation = $this->invitationService->getInvitation(2); + $this->invitationService->deleteInvitation($invitation); + + // Delete a session + $this->sessionService->deleteSession($session); + + return Command::SUCCESS; + } +} diff --git a/docs/content_management/collaborative_editing/collaborative_editing.md b/docs/content_management/collaborative_editing/collaborative_editing.md new file mode 100644 index 0000000000..6141b728cf --- /dev/null +++ b/docs/content_management/collaborative_editing/collaborative_editing.md @@ -0,0 +1,24 @@ +--- +description: Collaborative editing enables multiple users to work on the same content simultaneously. +page_type: landing_page +editions: + - lts-update +month_change: true +--- + +# Collaborative editing + +With Collaborative editing [LTS update](editions.md#lts-updates) multiple users can work on the same content created in [[= product_name =]] simultaneously, streamlining the content creation and review process. + +Users can invite both internal and external collaborators to a session, giving them access for editing or previewing. + +Additionaly, they can collaborate using a Real-time collaboration, an advanced part of the collaboration feature, to write and review content in a live mode thanks to CKEditor. +Real-time collaboration syncs changes instantly and shows user avatars and colored tags to indicate who is editing specific part of the Rich Text field. + +This feature also introduces new dashboard tabs for managing shared drafts and joining collaboration sessions easily. + +[[= cards([ +"content_management/collaborative_editing/collaborative_editing_guide", +"content_management/collaborative_editing/install_collaborative_editing", +"content_management/collaborative_editing/collaborative_editing_api" +], columns=3) =]] \ 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..b05e996558 --- /dev/null +++ b/docs/content_management/collaborative_editing/collaborative_editing_api.md @@ -0,0 +1,194 @@ +--- +description: Use PHP API to manage invitations, sessions, and participants while using collaborative editing feature. +editions: + - lts-update +month_change: true +--- + +# Collaborative editing API + +[[= product_name =]]'s Collaborative editing API provides two services for managing sessions and invitations, which differ in function: + +- [`InvitationServiceInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-InvitationServiceInterface.html) is used to manage collaboration sessions invitations + +- [`SessionServiceInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html) is used to manage collaboration sessions + +## Managing sessions + +### Create session + +You can create new content collaboration session with [`SessionService::createSession()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html#method_createSession): + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 69, 81) =]] +``` + +### Get session + +You can get an existing collaboration session by using: + +- Session id - with [`SessionService::getSession()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html#method_getSession) + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 82, 83) =]] +``` + +- Token - with [`SessionService::getSessionByToken()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html#method_getSessionByToken) + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 83, 84) =]] +``` + +### Find sessions + +You can find an existing session with [`SessionService::findSessions()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html#method_findSessions) by passing a SessionQuery object: + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 86, 89) =]] +``` + +### Update session + +You can update existing invitation with [`SessionService::updateSession()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html#method_updateSession): + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 90, 95) =]] +``` + +### Delete session + +You can delete session with [`SessionService::deleteSession()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html#method_deleteSession): + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 169, 170) =]] +``` + +## Managing participants + +### Add participant + +You can add participant to the collaboration session with [`SessionService::addParticipant()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html#method_addParticipant): + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 102, 116) =]] +``` + +You can add internal participants - users who already have an account in the [[= product_name =]], or external ones. +External participant can only be invited using their email address. + +The `ContentSessionScope::VIEW` constant defines the type of access granted to a participant. +Available scopes are: + +- `VIEW` - read-only access, used for preview purposes +- `EDIT` - grants editing capabilities + +### Get and update participant + +You can update participant added to the collaboration session with [`SessionService::updateParticipant()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html#method_updateParticipant). +The example below updates participant's permissions to allow for editing of shared content, not only previewing. + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 117, 125) =]] +``` +### Remove participant + +You can remove participant from the collaboration session with [`SessionService::removeParticipant()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html#method_removeParticipant): + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 126, 127) =]] +``` + +### Check session owner + +You can check the owner of the collaboration session with [`SessionService::isSessionOwner()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceDecorator.html#method_isSessionOwner): + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 129, 134) =]] +``` + +If no user is provided, current user is used. + +### Check session participant + +You can check whether a user belongs to a collaboration session with [`SessionService::isSessionParticipant()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-SessionServiceInterface.html#method_isSessionParticipant): + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 135, 140) =]] +``` + +## Managing invitations + +### Manage invitation + +You can get an invitation with [`InvitationService::getInvitation()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-InvitationServiceInterface.html#method_getInvitation): + + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 141, 150) =]] +``` + +You can select the property that you can read from an invitation: + +- Invitation ID: + +``` php + $invitation->getId(); +``` + +- Session ID: + +``` php + $invitation->getSession()->getId(); +``` + +- Participant ID: + +``` php + $invitation->getParticipant()->getId(); +``` + +- Invitation status: + +``` php + $invitation->getStatus(); +``` + +### Create invitation + +You can create new invitation for the collaborative session using the [`InvitationService::createInvitation()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-InvitationServiceInterface.html#method_createInvitation) method: + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 151, 158) =]] +``` + +You can use it when [auto-inviting participants](install_collaborative_editing.md#configuration) is not enabled. + +### Update invitation + +You can update existing invitation with [`InvitationService::updateInvitation()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-InvitationServiceInterface.html#method_updateInvitation): + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 159, 164) =]] +``` + +### Delete invitation + +You can delete an invitation with [`InvitationService::deleteInvitation()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-InvitationServiceInterface.html#method_deleteInvitation): + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php', 165, 168) =]] +``` + +### Find invitations + +You can find an invitation with [`InvitationService::findInvitations()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Collaboration-InvitationServiceInterface.html#method_findInvitations). + +To learn more about the available search options, see Search Criteria and Sort Clauses for Collaborative editing. + +## Example API usage + +Below you can see an example of API usage for Collaborative editing: + +``` php +[[= include_file('code_samples/collaboration/src/Command/ManageSessionsCommand.php') =]] +``` \ No newline at end of file diff --git a/docs/content_management/collaborative_editing/install_collaborative_editing.md b/docs/content_management/collaborative_editing/install_collaborative_editing.md new file mode 100644 index 0000000000..8fed8410c1 --- /dev/null +++ b/docs/content_management/collaborative_editing/install_collaborative_editing.md @@ -0,0 +1,174 @@ +--- +description: Install the Collaborative editing LTS update. +editions: + - lts-update +month_change: true +--- + +# Install Collaborative editing + +Collaborative editing feature is available as an [LTS update](editions.md#lts-updates) to [[= product_name =]] starting with version v4.6.24 or higher, regardless of its edition. +To use this feature you must first install the packages and configure them. + +## Install packages + +Run the following commands to install the packages: + +``` bash +composer require ibexa/collaboration +composer require ibexa/share +``` + +If you have an arrangements with [[= product_name_base =]] to use Real-time editing feature, you also need to install following package: + +``` bash +composer require ibexa/fieldtype-richtext-rte +``` + +This command instals also `ibexa/ckeditor-premium` package and adds the new real-time editing functionality to the Rich Text field type. +It also modifies the permission system to account for the new functionality. + +## Configure Collaborative editing + +Once the packages are installed, before you can start Collaborative editing feature, you must enable it by following these instructions. + +### Configuration + +You can configure Collaborative editing per [Repository](repository_configuration.md). + +Under `ibexa.repositories..collaboration` [configuration key](configuration.md#configuration-files), indicate the settings for collaboration: + +``` yaml +ibexa: + repositories: + : + collaboration: + participants: + allowed_types: + - internal + - external + auto_invite: + session: + public_link_enabled: +``` + +The following settings are available: + +- participants: + - `allowed_types` - defines allowed user types, values: `internal`, `external`, you can set one or both of the values + - `auto_invite` - determines whether invitations should be sent automatically when inviting someone to a session, default value: `true`, available values: `true`, `false` +- session: + - `public_link_enabled` - determines whether the public link is available, default value: `false`, available values: `true`, `false` + +#### `ibexa\share` configuration + +To share content model, you need to configure the `ibexa\share` package. +Under `ibexa.system` [configuration key](configuration.md#configuration-files), indicate the settings: + +``` yaml +ibexa: + system: + admin_group: + share: + content_type_groups: + - 'Content' + excluded_content_types: + - 'tag' + - 'product_category_tag' +``` + +The following setting is available: + +- `content_type_groups` – defines groups of content types for which the **Share** button is displayed (it can still be disabled for specific content types within these groups) + +In the example confugiration above, the **Share** button is displayed for any content that belongs to the `Content` group, except for `tag` and `product_category_tag` content types. + +### Add tables to the database + +Add the tables to the database: +Create the `ibexa_share.sql` file that contains the following code: + +=== "MySQL" + + ``` sql + [[= include_file('code_samples/collaboration/config/mysql/ibexa_share.sql', 0, None, ' ') =]] + ``` + +=== "PostgreSQL" + + ``` sql + [[= include_file('code_samples/collaboration/config/postgresql/ibexa_share.sql', 0, None, ' ') =]] + ``` + +Then, run the following command, where `` is the same name that you defined when you [installed](install_ibexa_dxp.md#change-installation-parameters) [[= product_name =]]: + +=== “MySQL” + +```bash +mysql -u -p < ibexa_share.sql +``` + +=== “PostgreSQL” + +```bash +psql < ibexa_share.sql +``` + +This command modifies the existing database schema by adding database configuration required for using Collaborative editing. + +### Modify the bundles file + +Then, if not using Symfony Flex, add the following code to the `config/bundles.php` file: + +``` php + ['all' => true], + Ibexa\Bundle\Share\IbexaShareBundle::class => ['all' => true], + Ibexa\Bundle\FieldTypeRichTextRTE\IbexaFieldTypeRichTextRTEBundle::class => ['all' => true], + Ibexa\Bundle\CkeditorPremium\IbexaCkeditorPremiumBundle::class => [‘all’ => true], +]; +``` + +### Add migration file and execute migration + +Last step is to add migration file and execute migration with the following commands: + +``` bash +php bin/console ibexa:migrations:import vendor/ibexa/collaboration/src/bundle/Resources/migrations/2025_08_26_10_14_shareable_user.yaml +php bin/console ibexa:migrations:migrate --file=2025_08_26_10_14_shareable_user.yaml +``` + +### Security configuration + +After an installation process is finished, go to `config/packages/security.yaml` and make following changes: + +- uncomment following lines with `shared` user provider under the `providers` key: + +```yaml +```suggestion +security: + providers: + # ... + shared: + id: Ibexa\Collaboration\Security\User\ShareableLinkUserProvider +``` + +- uncomment following lines under the `ibexa_shareable_link` key: + +```yaml +security: + # ... + ibexa_shareable_link: + request_matcher: Ibexa\Collaboration\Security\RequestMatcher\ShareableLinkRequestMatcher + pattern: ^/ + provider: shared + stateless: true + user_checker: Ibexa\Core\MVC\Symfony\Security\UserChecker + guard: + authenticator: Ibexa\Collaboration\Security\Authenticator\ShareableLinkAuthenticator +``` + +You can now restart you application and start [working with the Collaborative editing feature]([[= user_doc =]]/content_management/collaborative_editing/work_with_collaborative_editing/). \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 18d45253a0..4d7c93b0e5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -283,7 +283,10 @@ 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: content_management/collaborative_editing/collaborative_editing.md - Collaborative editing product guide: content_management/collaborative_editing/collaborative_editing_guide.md + - Install and configure Collaborative editing: content_management/collaborative_editing/install_collaborative_editing.md + - Collaborative editing API: content_management/collaborative_editing/collaborative_editing_api.md - Templating: - Templating: templating/templating.md - Render content: