Skip to content

Install Collaborative editing

Collaborative editing feature is available as an LTS update to Ibexa DXP 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:

1
2
composer require ibexa/collaboration
composer require ibexa/share

If you have an arrangements with Ibexa to use Real-time editing feature, you also need to install following package:

1
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.

Add tables to the database

Add the tables to the database: Create the ibexa_share.sql file that contains the following code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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;
CREATE TABLE ibexa_collaboration_content 
(
  id INT NOT NULL,
  content_id INT NOT NULL,
  version_no INT NOT NULL,
  language_id BIGINT NOT NULL,
  INDEX ibexa_collaboration_session_content_version_language_idx (content_id, version_no, language_id),
  PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8 COLLATE `utf8mb4_unicode_520_ci` ENGINE = InnoDB;
ALTER TABLE
  ibexa_collaboration_content
ADD
  CONSTRAINT ibexa_collaboration_content_pk FOREIGN KEY (id) REFERENCES ibexa_collaboration (id) ON UPDATE CASCADE ON DELETE CASCADE;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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;
CREATE TABLE ibexa_collaboration_content 
(
    id INT NOT NULL, 
    content_id INT NOT NULL, 
    version_no INT NOT NULL, 
    language_id BIGINT NOT NULL, 
    PRIMARY KEY (id)
);

CREATE INDEX ibexa_collaboration_session_content_version_language_idx ON ibexa_collaboration_content (content_id, version_no, language_id);

ALTER TABLE ibexa_collaboration_content
    ADD CONSTRAINT ibexa_collaboration_content_pk FOREIGN KEY (id) REFERENCES ibexa_collaboration (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE;

Then, run the following command, where <database_name> is the same name that you defined when you installed Ibexa DXP:

1
mysql -u <username> -p <password> <database_name> < ibexa_share.sql
1
psql <database_name> < 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:

1
2
3
4
5
6
7
8
9
<?php

return [
    // A lot of bundles…
    Ibexa\Bundle\Collaboration\IbexaCollaborationBundle::class => ['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:

1
2
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

Configure Collaborative editing

Once the packages are installed, before you can start Collaborative editing feature, you must enable it by following these instructions.

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:
1
2
3
4
5
security:
    providers:
        # ...
        shared:
            id: Ibexa\Collaboration\Security\User\ShareableLinkUserProvider
  • uncomment following lines under the ibexa_shareable_link key:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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

Configuration

You can configure Collaborative editing per Repository.

Under ibexa.repositories.<repository_name>.collaboration configuration key, indicate the settings for collaboration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ibexa:
    repositories:
        <repository_name>:
            collaboration:
                participants:
                    allowed_types:
                        - internal
                        - external
                    auto_invite: <value>
                session:
                    public_link_enabled: <value>

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, indicate the settings:

1
2
3
4
5
6
7
8
9
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.

You can now restart you application and start working with the Collaborative editing feature.