Skip to content
14 changes: 10 additions & 4 deletions src/wp-admin/options-writing.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,16 @@
<tr>
<th scope="row"><?php _e( 'Collaboration' ); ?></th>
<td>
<label for="wp_collaboration_enabled">
<input name="wp_collaboration_enabled" type="checkbox" id="wp_collaboration_enabled" value="1" <?php checked( '1', (bool) get_option( 'wp_collaboration_enabled' ) ); ?> />
<?php _e( 'Enable real-time collaboration' ); ?>
</label>
<?php if ( ! defined( 'WP_ALLOW_COLLABORATION' ) || true === WP_ALLOW_COLLABORATION ) : ?>
<div class="notice notice-warning inline">
<p><?php _e( '<strong>Note:</strong> Real-time collaboration has been disabled.' ); ?></p>
</div>
<?php else : ?>
<label for="wp_collaboration_enabled">
<input name="wp_collaboration_enabled" type="checkbox" id="wp_collaboration_enabled" value="1" <?php checked( '1', (bool) get_option( 'wp_collaboration_enabled' ) ); ?> />
<?php _e( 'Enable real-time collaboration' ); ?>
</label>
<?php endif; ?>
</td>
</tr>
<?php
Expand Down
21 changes: 20 additions & 1 deletion src/wp-includes/collaboration.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@
* @since 7.0.0
*/

/**
* Determines whether real-time collaboration is enabled.
*
* If the WP_ALLOW_COLLABORATION constant is false,
* collaboration is always disabled regardless of the database option.
* Otherwise, falls back to the 'wp_collaboration_enabled' option.
Comment on lines +12 to +14
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a potential edge case to verify here: if wp_is_collaboration_enabled() is called before WP_ALLOW_COLLABORATION is defined, RTC will also be disabled regardless of the database option.

Can we verify that this function can't be called before the constant is defined? If we can't verify this, or we found a way to call wp_is_collaboration_enabled() before the constant was declared, can we amend the docs to make this edge case clear?

Copy link
Contributor

@adamziel adamziel Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant is defined in wp_functionality_constants(); which is called in wp-settings.php (after require "collaboration.php") so the premature reference would have to happen at some point in the WordPress boot flow before that call (or via SHORTINIT). Two possible solutions I see are:

  • Accept that edge-case, since the same gotcha is true for other constants defined in that function: AUTOSAVE_INTERVAL, EMPTY_TRASH_DAYS, WP_POST_REVISIONS, and WP_CRON_LOCK_TIMEOUT.
  • Define the constant before the function is defined, e.g. directly in collaboration.php, maybe even inside of that function if it's not defined by the time of the call. That would look something like this:
function wp_is_collaboration_enabled() {
	if ( ! defined( 'WP_ALLOW_COLLABORATION' ) ) {
		$env_value = getenv( 'WP_ALLOW_COLLABORATION' );
		if ( false !== $env_value ) {
			define( 'WP_ALLOW_COLLABORATION', 'true' === $env_value );
		} else {
			// Environment variable is not defined, default to true.
			define( 'WP_ALLOW_COLLABORATION', true );
		}
	}

	if ( ! WP_ALLOW_COLLABORATION ) {
		return false
	}

	return (bool) get_option( 'wp_collaboration_enabled' );
}

Which would be fine with me. There's a precedence of a similar behavior in kses.php:

https://github.com/alecgeatches/wordpress-develop/blob/be03ad089eae148e2d95e429579022bef69c77c9/src/wp-includes/kses.php#L32-L53

Copy link
Contributor

@adamziel adamziel Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we keep this part as it is for beta6 and revisit it in RC1 cc @ellatrix

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming that this makes sense to me 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant is defined in wp_functionality_constants(); which is called in wp-settings.php (after require "collaboration.php") so the premature reference would have to happen at some point in the WordPress boot flow before that call (or via SHORTINIT). Two possible solutions I see are:

Thanks for catching this, I hadn't noticed the order of operations.

Of the two suggestions I think defining the constant in wp_is_collaboration_enabled() is the safest approach for RC1. @adamziel are you able to look in to this in #11322 (I'm at a mate's 50th today so won't have a chance, sorry).

*
* @since 7.0.0
*
* @return bool Whether real-time collaboration is enabled.
*/
function wp_is_collaboration_enabled() {
if ( ! defined( 'WP_ALLOW_COLLABORATION' ) || ! WP_ALLOW_COLLABORATION ) {
return false;
}

return (bool) get_option( 'wp_collaboration_enabled' );
}

/**
* Injects the real-time collaboration setting into a global variable.
*
Expand All @@ -18,7 +37,7 @@
function wp_collaboration_inject_setting() {
global $pagenow;

if ( ! (bool) get_option( 'wp_collaboration_enabled' ) ) {
if ( ! wp_is_collaboration_enabled() ) {
return;
}

Expand Down
20 changes: 20 additions & 0 deletions src/wp-includes/default-constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,26 @@ function wp_functionality_constants() {
if ( ! defined( 'WP_CRON_LOCK_TIMEOUT' ) ) {
define( 'WP_CRON_LOCK_TIMEOUT', MINUTE_IN_SECONDS );
}

/**
* Whether real time collaboration is permitted to be enabled.
*
* @since 7.0.0
*/
if ( ! defined( 'WP_ALLOW_COLLABORATION' ) ) {
$env_value = getenv( 'WP_ALLOW_COLLABORATION' );
if ( false === $env_value ) {
// Environment variable is not defined, default to allowing collaboration.
define( 'WP_ALLOW_COLLABORATION', true );
} else {
/*
* Environment variable is defined, let's confirm it is actually set to
* "true" as it may still have a string value "false" – the preceeding
* `if` branch only tests for the boolean `false`.
*/
define( 'WP_ALLOW_COLLABORATION', 'true' === $env_value );
}
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/wp-includes/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ function create_initial_post_types() {
)
);

if ( (bool) get_option( 'wp_collaboration_enabled' ) ) {
if ( wp_is_collaboration_enabled() ) {
register_post_type(
'wp_sync_storage',
array(
Expand Down Expand Up @@ -8672,7 +8672,7 @@ function wp_create_initial_post_meta() {
)
);

if ( (bool) get_option( 'wp_collaboration_enabled' ) ) {
if ( wp_is_collaboration_enabled() ) {
register_meta(
'post',
'_crdt_document',
Expand Down
2 changes: 1 addition & 1 deletion src/wp-includes/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ function create_initial_rest_routes() {
$icons_controller->register_routes();

// Collaboration.
if ( (bool) get_option( 'wp_collaboration_enabled' ) ) {
if ( wp_is_collaboration_enabled() ) {
$sync_storage = new WP_Sync_Post_Meta_Storage();
$sync_server = new WP_HTTP_Polling_Sync_Server( $sync_storage );
$sync_server->register_routes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ public function create_item( $request ) {
* the saved post. This diff is then applied to the in-memory CRDT
* document, which can lead to duplicate inserts or deletions.
*/
$is_collaboration_enabled = (bool) get_option( 'wp_collaboration_enabled' );
$is_collaboration_enabled = wp_is_collaboration_enabled();

if ( $is_draft && (int) $post->post_author === $user_id && ! $post_lock && ! $is_collaboration_enabled ) {
/*
Expand Down
Loading