Recommended and optional Real-time Collaboration configuration options

TinyMCE’s Real-time Collaboration (RTC) plugin will be retired and deactivated on December 31, 2023, and is no longer available for purchase.

This section covers the recommended and optional configuration options for the RTC plugin. None of these options are required but assist with creating a consistent user experience between your application and TinyMCE Real-time Collaboration (RTC).

For the best user experience, Tiny recommends including these configuration options:

rtc_server_disconnected

If the RTC session fails to connect, or is disconnected due to an error, the user will be blocked from editing (using setProgressState) along with an error notification:

warning iconRTC has lost connection to the server, please reload the page. More information may be available in the console.

RTC disconnected error example

The rtc_server_disconnected callback can be used to provide an alternative response to this condition.

Required plugin

Real-time Collaboration (rtc)

Type

Function

Input parameters
Field Type Description

reason

string

The cause of the disconnection. The value will be one of the reasons described below.

message

string

A suggested description for the error, translated into the active user interface language, suitable for displaying to a user. This string may contain HTML, and in some cases is the same string displayed in the editor notification.

Reasons for disconnection

It is critical to at least handle the client_update_required reason. This indicates the RTC plugin in the current editor instance is out-of-date compared to other users on the session. The behavior in this scenario depends on the configuration: * If the rtc_server_disconnected is set, no message is displayed to the user for this error. It is up to the integrator to manage cleanly reloading the page. * If the rtc_server_disconnected is not set, the suggested error message will be displayed in a notification asking the user to reload the page.

The reason field will have one of the following values.

client_update_required

This error indicates the RTC plugin is out-of-date and cannot connect to an active session for the supplied rtc_document_id. This can happen on startup, but is more common at runtime during editor upgrades. The suggested message recommends the user reload the page.

encryption

Indicates a failure at startup either: in the cryptography process or the configured rtc_encryption_provider function. This may be caused by an error in the editor configuration.

jwt

Indicates a problem with the configured rtc_token_provider function. Either the provider returned a rejected promise, the returned object structure was incorrect, or the token was invalid.

content

Indicates a problem with the configured rtc_initial_content_provider function. Either the provider returned a rejected promise or the returned object structure was incorrect.

general

A generic error for reasons that do not yet have a category. Details will be printed to the browser console.

Example of handling server disconnection

tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: () => Promise.resolve({ token: 'signed-JWT-token' }),
  rtc_server_disconnected: ({ reason, message }) => {
    // perform some action in response to the RTC session disconnecting, such as:
    tinymce.activeEditor.notificationManager.open({
      text: 'Disconnected: ' + reason + '\n' + message,
      type: 'error'
    });
  }
});

rtc_user_details_provider

By default, a user’s unique ID (userId, the sub field from their JWT) will be displayed as the username in remote caret tooltips.

To display a descriptive name on the caret, the userId needs to be resolved into user details that include the user’s display name (fullName). This resolution is done on each client to avoid sending any personal information through the RTC server.

Only the fullName is required, but the whole object will be stored within the RTC caret information for use with the rtc_client_connected and rtc_client_disconnected callbacks to avoid duplicate lookup queries. The user details are also passed to the RtcClientConnected and RtcClientDisconnected events.

This provider function is called once for each connecting client. Clients that reconnect may trigger a new call to the provider function rather than using cached data.

Required plugin

Real-time Collaboration (rtc)

Type

Function (Returns a Promise)

Input parameters
Field Type Description

userId

string

User ID to resolve into user details.

Return data
Field Type Description

fullName

string

The full display name of user. For example: "John Doe".

any custom field

any

Extra user data for use in the rtc_client_connected API.

Example of providing static user details

tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: () => Promise.resolve({ token: 'signed-JWT-token' }),
  rtc_user_details_provider: ({ userId }) => Promise.resolve({ fullName: "John Doe" })
});

Example of providing user details from your server

tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: () => Promise.resolve({ token: 'signed-JWT-token' }),
  rtc_user_details_provider: ({ userId }) =>
    fetch('/getUserDetails', {
      method: 'POST',
      credentials: 'include',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ userId })
    })
    .then((response) => response.json())
    .then(({ fullnamefromserver })) => ({ fullName: fullnamefromserver })
    .catch((error) => console.log('Failed to retrieve user details\n' + error))
});

Optional configuration options

The following configuration options have been provide to assist with integrating Real-time Collaboration (RTC) into webpages and applications:

rtc_snapshot

Real-time collaboration integrations regularly store the content, eliminating the need for a save button. The TinyMCE RTC plugin provides a version number to assist with storing the HTML content snapshots. These snapshots are not stored on the RTC server and must be handled by the integrator.

For any given document ID, the server guarantees the version number will only increase. It can be safely used for conflict resolution. For each document ID and version combination, the snapshot content is guaranteed to be identical.

The snapshot callback will be executed in response to local changes, with access to the serialized editor content. The content is retrieved through a getContent function to reduce CPU load if the callback decides to not use the editor content.

Required plugin

Real-time Collaboration (rtc)

Type

Function

Input parameters
Field Type Description

documentId

string

The document ID from the rtc_document_id option.

version

integer

An increasing version number, specific to the current document ID, between 0 and 2147483648 (231).

getContent

Function

Render the content for this specific version to HTML.

Example of getting content snapshots

tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: () => Promise.resolve({ token: 'signed-JWT-token' }),
  rtc_snapshot: ({ version, getContent }) => {
    console.log('Current version', version);
    console.log('HTML', getContent());
  }
});

rtc_initial_content_provider

By default, the initial editor content is retrieved from the element targeted using the TinyMCE selector option.

The rtc_initial_content_provider option allows alternative initial content be retrieved for a new RTC session. This option works with frameworks and integrations (such as the TinyMCE integrations) that don’t provide access to the target element directly. If the target element contains content and a rtc_initial_content_provider has been provided, the content from the rtc_initial_content_provider will be used as the initial editor content.

The rtc_initial_content_provider is used for new documents (documentId) and is not for opening or reopening existing documents. If the document already exists, the content on the RTC Server with the provided documentId will be loaded into the editor when it is intitalized.

Required plugin

Real-time Collaboration (rtc)

Type

Function (Returns a Promise)

Input parameters
Field Type Description

documentId

string

The document ID configured using the rtc_document_id option.

Return data
Field Type Description

content

string

String containing the HTML to be imported into the editor when there is no existing session.

Example of providing static content

tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: () => Promise.resolve({ token: 'signed-JWT-token' }),
  rtc_initial_content_provider: () => Promise.resolve({ content: "<p>Hello world!</p>" })
})

Example of providing dynamic content from the server

tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: () => Promise.resolve({ token: 'signed-JWT-token' }),
  rtc_initial_content_provider: ({ documentId }) => {
    return fetch(`/getContent/${documentId}`, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      }
    }).then((response) => response.json());
  }
});

rtc_client_info

The rtc_client_info option allows status flags from the local editor environment to be provided to other connecting clients. For example: "This user is on a mobile device". This option should not be used to communicate sensitive information; the authenticity of the data cannot be guaranteed.

This option accepts an object that must be serializable (JSON.stringify will be used to transmit it between clients). Other clients receive a copy of this object in their rtc_client_connected events.

Required plugin

Real-time Collaboration (rtc)

Type

Object

Example of client status information

tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: () => Promise.resolve({ token: 'signed-JWT-token' }),
  rtc_client_info: { onMobile: true, region: 'us' }
});

rtc_client_connected

This option allows applications to show when a user enters the RTC session. When used in combination with rtc_client_disconnected, a user interface of connected users can be kept up to date.

Only one rtc_client_connected event will be fired per client connection. Connecting to a session with multiple existing clients will fire separate rtc_client_connected events for each existing client. If a user connects using two or more editors (such as on desktop and on mobile), a separate rtc_client_connected event will be fired.

To help with generating a user interface for connected users, an object for user details is provided by the rtc_user_details_provider option.

Required plugin

Real-time Collaboration (rtc)

Type

Function

Input parameters
Field Type Description

userId

string

This is the user’s unique ID (the sub field from their JWT, which is also used for rtc_user_details_provider). Multiple connection events will be received with the same user ID if a user opens multiple sessions (for example on desktop and mobile).

userDetails

object

This is a copy of the object returned by rtc_user_details_provider. RTC only uses the fullName field, but the entire object will be cloned and passed to rtc_client_connected.

If a user details provider is not configured, this will be an empty object.

clientId

string

This is a unique identifier, generated by the RTC protocol, that can be used to differentiate between the same user connecting multiple times.

caretNumber

integer

This will be a number between 1 and 8, corresponding to one of the 8 colors defined in TinyMCE CSS. TinyMCE supports 8 distinct caret colors. If more than 8 clients connect to a session, the numbers will be reused.

A custom skin is required to change these colors, and no more than 8 are supported. For information on creating a custom skin, see: Customizing the Editor UI.

clientInfo

object

This is a copy of the rtc_client_info data from the remote user’s editor configuration.

If none was configured, this will be an empty object.

TinyMCE cannot guarantee the accuracy of data which comes from a remote object. Tiny recommends only using the client information data for status flags. To obtain authentic client information, use the rtc_user_details_provider data returned through the userDetails field.

Example using client connect and disconnect events with custom user details

While all fields are provided to both the rtc_client_connected and rtc_client_disconnected functions, this example only handles the relevant fields for each callback function.

const connectedUsers = {}
tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: () => Promise.resolve({ token: 'signed-JWT-token' }),
  rtc_user_details_provider: ({userId}) => Promise.resolve({ fullName: "John Doe", jobTitle: "Engineer" }),
  rtc_client_connected: ({userDetails: {fullName, jobTitle}, userId, caretNumber, clientId, clientInfo}) => {
    connectedUsers[clientId] = {
      caretNumber,
      clientInfo,
      userDetails,
      userId
    }
    console.log(`${jobTitle} ${fullName} connected with caret number ${caretNumber}`, clientInfo)
  }
  rtc_client_disconnected: ({clientId, userDetails: {fullName, jobTitle}}) => {
    delete connectedUsers[clientId]
    console.log(`${jobTitle} ${fullName} disconnected`)
  }
})

rtc_client_disconnected

The rtc_client_disconnected option can be used with the rtc_client_connected option to maintain a list of connected users.

Required plugin

Real-time Collaboration (rtc)

Type

Function

Input parameters
Field Type Description

userId

string

This is the user’s unique ID (the sub field from their JWT, which is also used for rtc_user_details_provider). Multiple connection events will be received with the same user ID if a user opens multiple sessions (for example on desktop and mobile).

userDetails

object

This is a copy of the object returned by rtc_user_details_provider. RTC only uses the fullName field, but the entire object will be cloned and passed to rtc_client_connected.

If a user details provider is not configured, this will be an empty object.

clientId

string

This is a unique identifier, generated by the RTC protocol, that can be used to differentiate between the same user connecting multiple times.

caretNumber

integer

This will be a number between 1 and 8, corresponding to one of the 8 colors defined in TinyMCE CSS. TinyMCE supports 8 distinct caret colors. If more than 8 clients connect to a session, the numbers will be reused.

A custom skin is required to change these colors, and no more than 8 are supported. For information on creating a custom skin, see: Customizing the Editor UI.

clientInfo

object

This is a copy of the rtc_client_info data from the remote user’s editor configuration.

If none was configured, this will be an empty object.

Example using client connect and disconnect events with custom user details

While all fields are provided to both the rtc_client_connected and rtc_client_disconnected functions, this example only handles the relevant fields for each callback function.

const connectedUsers = {}
tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: () => Promise.resolve({ token: 'signed-JWT-token' }),
  rtc_user_details_provider: ({userId}) => Promise.resolve({ fullName: "John Doe", jobTitle: "Engineer" }),
  rtc_client_connected: ({userDetails: {fullName, jobTitle}, userId, caretNumber, clientId, clientInfo}) => {
    connectedUsers[clientId] = {
      caretNumber,
      clientInfo,
      userDetails,
      userId
    }
    console.log(`${jobTitle} ${fullName} connected with caret number ${caretNumber}`, clientInfo)
  }
  rtc_client_disconnected: ({clientId, userDetails: {fullName, jobTitle}}) => {
    delete connectedUsers[clientId]
    console.log(`${jobTitle} ${fullName} disconnected`)
  }
})

rtc_unsupported_content_notification

The rtc_unsupported_content_notification option can be used to disable the notification about unsupported HTML inside the RTC enabled editor. Existing documents containing HTML generated using plugins not yet supported by the RTC plugin could lead to unexpected behavior. For a list of RTC supported plugins, see: Supported TinyMCE functionality for Real-time Collaboration.

Required plugin

Real-time Collaboration (rtc)

Type

Boolean

Default

true

Example of blocking unsupported content notifications

tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: () => Promise.resolve({ token: 'signed-JWT-token' }),
  rtc_unsupported_content_notification: false
});