diff --git a/docs/capabilities.md b/docs/capabilities.md
index 12ed3c4cb9..dbadbd1739 100644
--- a/docs/capabilities.md
+++ b/docs/capabilities.md
@@ -188,6 +188,7 @@
* `config => conversations => retention-instant-meetings` (local) - Number of days before an instant meeting conversation is deleted (`0` = disabled)
* `config => conversations => retention-phone` (local) - Number of days before an inactive incoming or outgoing phone conversation is deleted (`0` = disabled)
* `config => call => predefined-backgrounds-v2` (local) - Whether virtual backgrounds should be read from the theming directory
+* `config => experiments => enabled` (local) - Bit flag of enabled experiments for clients
* `dashboard-event-rooms` (local) - Whether Talk APIs offer functionality for Dashboard requests
* `mutual-calendar-events` (local) - Whether Talk APIs offer mutual calendar events for 1:1 rooms
* `upcoming-reminders` (local) - Whether the API to list upcoming reminders exists
diff --git a/docs/settings.md b/docs/settings.md
index 84148cfeef..a4c4351a3a 100644
--- a/docs/settings.md
+++ b/docs/settings.md
@@ -102,6 +102,8 @@ Legend:
| `retention_event_rooms` | int | `28` | No | | Retention period of event conversations in days (`0` means no-retention) |
| `retention_phone_rooms` | int | `7` | No | | Retention period of phone dial-in and dial-out conversations in days (`0` means no-retention) |
| `retention_instant_meetings` | int | `1` | No | | Retention period of instant meetings in days (`0` means no-retention) |
+| `experiments_users` | int | `0` | Yes | | Bit flag of experiments that should be enabled for logged-in users on this server (see [Experiments](#experiments) below) |
+| `experiments_guests` | int | `0` | Yes | | Bit flag of experiments that should be enabled for guests on this server (see [Experiments](#experiments) below) |
| `grid_videos_limit_enforced` | string
`yes` or `no` | `no` | No | | Whether the number of grid videos should be enforced |
| `changelog` | string
`yes` or `no` | `yes` | No | | Whether the changelog conversation is updated with new features on major releases |
| `has_reference_id` | string
`yes` or `no` | `no` | Yes | | Indicator whether the clients can use the reference value to identify their message, will be automatically set to `yes` when the repair steps are executed |
@@ -128,3 +130,11 @@ Legend:
| `backgrounds_branded_for_guests` | string
`1` or `0` | `0` | No | | Whether guests are allowed to use the virtual backgrounds provided via `themes/talk-backgrounds/` |
| `backgrounds_default_for_users` | string
`1` or `0` | `1` | No | | Whether users are allowed to use the default virtual backgrounds provided by the releases |
| `backgrounds_upload_users` | string
`1` or `0` | `1` | No | | Whether users are allowed to upload custom virtual backgrounds and choose from their Nextcloud Files |
+
+## Experiments
+
+Features that can be toggled on-off with the `experiments_users` and `experiments_guests` bit flags:
+
+| Bit | Status | Introduced | Ended | Description |
+|-----|--------|----------------------------------|-------|-----------------------------------------------------------------------------------------------------------------------------|
+| 1 | Active | Web 21.1.0
Desktop 1.2.2-beta | - | Instead of refreshing the participant list repeatingly during calls, the data is generated from received signaling messages |
diff --git a/lib/Capabilities.php b/lib/Capabilities.php
index 73ca815de1..194c1ff401 100644
--- a/lib/Capabilities.php
+++ b/lib/Capabilities.php
@@ -193,6 +193,9 @@ class Capabilities implements IPublicCapability {
'session-ping-limit',
'hello-v2-token-key',
],
+ 'experiments' => [
+ 'enabled',
+ ],
];
protected ICache $talkCache;
@@ -277,6 +280,9 @@ class Capabilities implements IPublicCapability {
'session-ping-limit' => max(0, (int)$this->serverConfig->getAppValue('spreed', 'session-ping-limit', '200')),
// 'hello-v2-token-key' => string,
],
+ 'experiments' => [
+ 'enabled' => max(0, $this->appConfig->getAppValueInt($user instanceof IUser ? 'experiments_users' : 'experiments_guests')),
+ ],
],
'config-local' => self::LOCAL_CONFIGS,
'version' => $this->appManager->getAppVersion('spreed'),
diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php
index cba46422ae..de634542c2 100644
--- a/lib/Controller/RoomController.php
+++ b/lib/Controller/RoomController.php
@@ -190,6 +190,12 @@ class RoomController extends AEnvironmentAwareOCSController {
$this->config->getAppValue('spreed', 'federation_allowed_groups', '[]'),
];
+ if ($this->userId !== null) {
+ $values[] = $this->appConfig->getAppValueInt('experiments_users');
+ } else {
+ $values[] = $this->appConfig->getAppValueInt('experiments_guests');
+ }
+
return [
'X-Nextcloud-Talk-Hash' => sha1(implode('#', $values)),
];
diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php
index ed4c28a784..8b96a68f5b 100644
--- a/lib/ResponseDefinitions.php
+++ b/lib/ResponseDefinitions.php
@@ -497,6 +497,9 @@ namespace OCA\Talk;
* session-ping-limit: int,
* hello-v2-token-key?: string,
* },
+ * experiments: array{
+ * enabled: non-negative-int,
+ * },
* },
* config-local: array>,
* version: string,
diff --git a/openapi-administration.json b/openapi-administration.json
index a15b611f13..45525338ff 100644
--- a/openapi-administration.json
+++ b/openapi-administration.json
@@ -120,7 +120,8 @@
"conversations",
"federation",
"previews",
- "signaling"
+ "signaling",
+ "experiments"
],
"properties": {
"attachments": {
@@ -349,6 +350,19 @@
"type": "string"
}
}
+ },
+ "experiments": {
+ "type": "object",
+ "required": [
+ "enabled"
+ ],
+ "properties": {
+ "enabled": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 0
+ }
+ }
}
}
},
diff --git a/openapi-backend-recording.json b/openapi-backend-recording.json
index 4a87c1fc61..17f5b9dcdd 100644
--- a/openapi-backend-recording.json
+++ b/openapi-backend-recording.json
@@ -53,7 +53,8 @@
"conversations",
"federation",
"previews",
- "signaling"
+ "signaling",
+ "experiments"
],
"properties": {
"attachments": {
@@ -282,6 +283,19 @@
"type": "string"
}
}
+ },
+ "experiments": {
+ "type": "object",
+ "required": [
+ "enabled"
+ ],
+ "properties": {
+ "enabled": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 0
+ }
+ }
}
}
},
diff --git a/openapi-backend-signaling.json b/openapi-backend-signaling.json
index cb0fdc3080..83591942fd 100644
--- a/openapi-backend-signaling.json
+++ b/openapi-backend-signaling.json
@@ -53,7 +53,8 @@
"conversations",
"federation",
"previews",
- "signaling"
+ "signaling",
+ "experiments"
],
"properties": {
"attachments": {
@@ -282,6 +283,19 @@
"type": "string"
}
}
+ },
+ "experiments": {
+ "type": "object",
+ "required": [
+ "enabled"
+ ],
+ "properties": {
+ "enabled": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 0
+ }
+ }
}
}
},
diff --git a/openapi-backend-sipbridge.json b/openapi-backend-sipbridge.json
index f011dcd64b..d0ef358218 100644
--- a/openapi-backend-sipbridge.json
+++ b/openapi-backend-sipbridge.json
@@ -96,7 +96,8 @@
"conversations",
"federation",
"previews",
- "signaling"
+ "signaling",
+ "experiments"
],
"properties": {
"attachments": {
@@ -325,6 +326,19 @@
"type": "string"
}
}
+ },
+ "experiments": {
+ "type": "object",
+ "required": [
+ "enabled"
+ ],
+ "properties": {
+ "enabled": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 0
+ }
+ }
}
}
},
diff --git a/openapi-bots.json b/openapi-bots.json
index 5f9062ebfd..5a529fd3bc 100644
--- a/openapi-bots.json
+++ b/openapi-bots.json
@@ -53,7 +53,8 @@
"conversations",
"federation",
"previews",
- "signaling"
+ "signaling",
+ "experiments"
],
"properties": {
"attachments": {
@@ -282,6 +283,19 @@
"type": "string"
}
}
+ },
+ "experiments": {
+ "type": "object",
+ "required": [
+ "enabled"
+ ],
+ "properties": {
+ "enabled": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 0
+ }
+ }
}
}
},
diff --git a/openapi-federation.json b/openapi-federation.json
index 9828ec19c5..015c90b509 100644
--- a/openapi-federation.json
+++ b/openapi-federation.json
@@ -96,7 +96,8 @@
"conversations",
"federation",
"previews",
- "signaling"
+ "signaling",
+ "experiments"
],
"properties": {
"attachments": {
@@ -325,6 +326,19 @@
"type": "string"
}
}
+ },
+ "experiments": {
+ "type": "object",
+ "required": [
+ "enabled"
+ ],
+ "properties": {
+ "enabled": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 0
+ }
+ }
}
}
},
diff --git a/openapi-full.json b/openapi-full.json
index fd9f8a407a..fd2af93a6d 100644
--- a/openapi-full.json
+++ b/openapi-full.json
@@ -254,7 +254,8 @@
"conversations",
"federation",
"previews",
- "signaling"
+ "signaling",
+ "experiments"
],
"properties": {
"attachments": {
@@ -483,6 +484,19 @@
"type": "string"
}
}
+ },
+ "experiments": {
+ "type": "object",
+ "required": [
+ "enabled"
+ ],
+ "properties": {
+ "enabled": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 0
+ }
+ }
}
}
},
diff --git a/openapi.json b/openapi.json
index cda328e506..67fa443fb4 100644
--- a/openapi.json
+++ b/openapi.json
@@ -213,7 +213,8 @@
"conversations",
"federation",
"previews",
- "signaling"
+ "signaling",
+ "experiments"
],
"properties": {
"attachments": {
@@ -442,6 +443,19 @@
"type": "string"
}
}
+ },
+ "experiments": {
+ "type": "object",
+ "required": [
+ "enabled"
+ ],
+ "properties": {
+ "enabled": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 0
+ }
+ }
}
}
},
diff --git a/src/__mocks__/capabilities.ts b/src/__mocks__/capabilities.ts
index 48fc9036bc..798ae1567a 100644
--- a/src/__mocks__/capabilities.ts
+++ b/src/__mocks__/capabilities.ts
@@ -178,6 +178,9 @@ export const mockedCapabilities: Capabilities = {
'session-ping-limit': 200,
'hello-v2-token-key': '123',
},
+ experiments: {
+ enabled: 0,
+ },
},
'config-local': {
attachments: [
@@ -215,6 +218,9 @@ export const mockedCapabilities: Capabilities = {
'session-ping-limit',
'hello-v2-token-key',
],
+ experiments: [
+ 'enabled',
+ ],
},
version: '20.0.0-dev.0',
}
diff --git a/src/types/openapi/openapi-administration.ts b/src/types/openapi/openapi-administration.ts
index f07945cba8..4d77d785f6 100644
--- a/src/types/openapi/openapi-administration.ts
+++ b/src/types/openapi/openapi-administration.ts
@@ -278,6 +278,10 @@ export type components = {
"session-ping-limit": number;
"hello-v2-token-key"?: string;
};
+ experiments: {
+ /** Format: int64 */
+ enabled: number;
+ };
};
"config-local": {
[key: string]: string[];
diff --git a/src/types/openapi/openapi-backend-recording.ts b/src/types/openapi/openapi-backend-recording.ts
index aa90fe07a9..964854ca67 100644
--- a/src/types/openapi/openapi-backend-recording.ts
+++ b/src/types/openapi/openapi-backend-recording.ts
@@ -112,6 +112,10 @@ export type components = {
"session-ping-limit": number;
"hello-v2-token-key"?: string;
};
+ experiments: {
+ /** Format: int64 */
+ enabled: number;
+ };
};
"config-local": {
[key: string]: string[];
diff --git a/src/types/openapi/openapi-backend-signaling.ts b/src/types/openapi/openapi-backend-signaling.ts
index 458a23c11c..89555db4dd 100644
--- a/src/types/openapi/openapi-backend-signaling.ts
+++ b/src/types/openapi/openapi-backend-signaling.ts
@@ -98,6 +98,10 @@ export type components = {
"session-ping-limit": number;
"hello-v2-token-key"?: string;
};
+ experiments: {
+ /** Format: int64 */
+ enabled: number;
+ };
};
"config-local": {
[key: string]: string[];
diff --git a/src/types/openapi/openapi-backend-sipbridge.ts b/src/types/openapi/openapi-backend-sipbridge.ts
index 675d655d27..49abf3f1f9 100644
--- a/src/types/openapi/openapi-backend-sipbridge.ts
+++ b/src/types/openapi/openapi-backend-sipbridge.ts
@@ -213,6 +213,10 @@ export type components = {
"session-ping-limit": number;
"hello-v2-token-key"?: string;
};
+ experiments: {
+ /** Format: int64 */
+ enabled: number;
+ };
};
"config-local": {
[key: string]: string[];
diff --git a/src/types/openapi/openapi-bots.ts b/src/types/openapi/openapi-bots.ts
index c7d3b0f5f7..ed9382a174 100644
--- a/src/types/openapi/openapi-bots.ts
+++ b/src/types/openapi/openapi-bots.ts
@@ -116,6 +116,10 @@ export type components = {
"session-ping-limit": number;
"hello-v2-token-key"?: string;
};
+ experiments: {
+ /** Format: int64 */
+ enabled: number;
+ };
};
"config-local": {
[key: string]: string[];
diff --git a/src/types/openapi/openapi-federation.ts b/src/types/openapi/openapi-federation.ts
index 81f3dd82a9..9539e8e2f8 100644
--- a/src/types/openapi/openapi-federation.ts
+++ b/src/types/openapi/openapi-federation.ts
@@ -224,6 +224,10 @@ export type components = {
"session-ping-limit": number;
"hello-v2-token-key"?: string;
};
+ experiments: {
+ /** Format: int64 */
+ enabled: number;
+ };
};
"config-local": {
[key: string]: string[];
diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts
index e95af6dc34..6091d318d3 100644
--- a/src/types/openapi/openapi-full.ts
+++ b/src/types/openapi/openapi-full.ts
@@ -2234,6 +2234,10 @@ export type components = {
"session-ping-limit": number;
"hello-v2-token-key"?: string;
};
+ experiments: {
+ /** Format: int64 */
+ enabled: number;
+ };
};
"config-local": {
[key: string]: string[];
diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts
index c33c9522d2..080fd159ac 100644
--- a/src/types/openapi/openapi.ts
+++ b/src/types/openapi/openapi.ts
@@ -1712,6 +1712,10 @@ export type components = {
"session-ping-limit": number;
"hello-v2-token-key"?: string;
};
+ experiments: {
+ /** Format: int64 */
+ enabled: number;
+ };
};
"config-local": {
[key: string]: string[];
diff --git a/tests/php/CapabilitiesTest.php b/tests/php/CapabilitiesTest.php
index 0a2efe7da8..0be528c9fb 100644
--- a/tests/php/CapabilitiesTest.php
+++ b/tests/php/CapabilitiesTest.php
@@ -115,6 +115,7 @@ class CapabilitiesTest extends TestCase {
['retention_event_rooms', 28, 28],
['retention_phone_rooms', 7, 7],
['retention_instant_meetings', 1, 1],
+ ['experiments_guests', 0, 0],
]);
$this->assertInstanceOf(IPublicCapability::class, $capabilities);
@@ -195,6 +196,9 @@ class CapabilitiesTest extends TestCase {
'signaling' => [
'session-ping-limit' => 200,
],
+ 'experiments' => [
+ 'enabled' => 0,
+ ],
],
'config-local' => Capabilities::LOCAL_CONFIGS,
'version' => '1.2.3',
@@ -280,6 +284,7 @@ class CapabilitiesTest extends TestCase {
['retention_event_rooms', 28, 28],
['retention_phone_rooms', 7, 7],
['retention_instant_meetings', 1, 1],
+ ['experiments_users', 0, 0],
]);
$this->assertInstanceOf(IPublicCapability::class, $capabilities);
@@ -363,6 +368,9 @@ class CapabilitiesTest extends TestCase {
'signaling' => [
'session-ping-limit' => 50,
],
+ 'experiments' => [
+ 'enabled' => 0,
+ ],
],
'config-local' => Capabilities::LOCAL_CONFIGS,
'version' => '1.2.3',