Skip to content

Commit d92b691

Browse files
committed
feat(settings): Introduce conversation presets
Signed-off-by: Joas Schilling <[email protected]>
1 parent 6f27c0f commit d92b691

File tree

9 files changed

+285
-36
lines changed

9 files changed

+285
-36
lines changed

lib/Controller/RoomController.php

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
use OCA\Talk\Model\Session;
5454
use OCA\Talk\Model\Thread;
5555
use OCA\Talk\Participant;
56+
use OCA\Talk\RoomPresets\Parameter;
57+
use OCA\Talk\RoomPresets\Preset;
58+
use OCA\Talk\Service\RoomPresetService;
5659
use OCA\Talk\ResponseDefinitions;
5760
use OCA\Talk\Room;
5861
use OCA\Talk\Service\BanService;
@@ -153,6 +156,7 @@ public function __construct(
153156
protected IURLGenerator $url,
154157
protected IL10N $l,
155158
protected ThreadService $threadService,
159+
protected RoomPresetService $presetService,
156160
) {
157161
parent::__construct($appName, $request);
158162
}
@@ -590,32 +594,32 @@ protected function formatRoom(
590594
* In case the `$roomType` is {@see Room::TYPE_ONE_TO_ONE} only the `$invite`
591595
* or `$participants` parameter is supported.
592596
*
593-
* @param int $roomType Type of the room
597+
* @param ?int $roomType Type of the room
594598
* @psalm-param Room::TYPE_* $roomType
595599
* @param string $invite User, group, … ID to invite @deprecated Use the `$participants` array instead
596600
* @param string $roomName Name of the room, unless the legacy mode providing `$invite` and `$source` is used, the name must no longer be empty with the `conversation-creation-all` capability (Ignored if `$roomType` is {@see Room::TYPE_ONE_TO_ONE})
597601
* @param 'groups'|'circles'|'' $source Source of the invite ID ('circles' to create a room with a circle, etc.) @deprecated Use the `$participants` array instead
598602
* @param string $objectType Type of the object (Ignored if `$roomType` is {@see Room::TYPE_ONE_TO_ONE})
599603
* @param string $objectId ID of the object (Ignored if `$roomType` is {@see Room::TYPE_ONE_TO_ONE})
600604
* @param string $password The room password (only available with `conversation-creation-password` capability) (Ignored if `$roomType` is not {@see Room::TYPE_PUBLIC})
601-
* @param 0|1 $readOnly Read only state of the conversation (Default writable) (only available with `conversation-creation-all` capability)
602-
* @psalm-param Room::READ_* $readOnly
603-
* @param 0|1|2 $listable Scope where the conversation is listable (Default not listable for anyone) (only available with `conversation-creation-all` capability)
604-
* @psalm-param Room::LISTABLE_* $listable
605-
* @param int $messageExpiration Seconds after which messages will disappear, 0 disables expiration (Default 0) (only available with `conversation-creation-all` capability)
606-
* @psalm-param non-negative-int $messageExpiration
607-
* @param 0|1 $lobbyState Lobby state of the conversation (Default lobby is disabled) (only available with `conversation-creation-all` capability)
608-
* @psalm-param Webinary::LOBBY_* $lobbyState
605+
* @param 0|1|null $readOnly Read only state of the conversation (Default writable) (only available with `conversation-creation-all` capability)
606+
* @psalm-param ?Room::READ_* $readOnly
607+
* @param 0|1|2|null $listable Scope where the conversation is listable (Default not listable for anyone) (only available with `conversation-creation-all` capability)
608+
* @psalm-param ?Room::LISTABLE_* $listable
609+
* @param ?int $messageExpiration Seconds after which messages will disappear, 0 disables expiration (Default 0) (only available with `conversation-creation-all` capability)
610+
* @psalm-param ?non-negative-int $messageExpiration
611+
* @param 0|1|null $lobbyState Lobby state of the conversation (Default lobby is disabled) (only available with `conversation-creation-all` capability)
612+
* @psalm-param ?Webinary::LOBBY_* $lobbyState
609613
* @param int|null $lobbyTimer Timer when the lobby will be removed (Default null, will not be disabled automatically) (only available with `conversation-creation-all` capability)
610-
* @psalm-param non-negative-int|null $lobbyTimer
611-
* @param 0|1|2 $sipEnabled Whether SIP dial-in shall be enabled (only available with `conversation-creation-all` capability)
612-
* @psalm-param Webinary::SIP_* $sipEnabled
613-
* @param int<0, 255> $permissions Default permissions for participants (only available with `conversation-creation-all` capability)
614-
* @psalm-param int-mask-of<Attendee::PERMISSIONS_*> $permissions
615-
* @param 0|1 $recordingConsent Whether participants need to agree to a recording before joining a call (only available with `conversation-creation-all` capability)
616-
* @psalm-param RecordingService::CONSENT_REQUIRED_NO|RecordingService::CONSENT_REQUIRED_YES $recordingConsent
617-
* @param 0|1 $mentionPermissions Who can mention at-all in the chat (only available with `conversation-creation-all` capability)
618-
* @psalm-param Room::MENTION_PERMISSIONS_* $mentionPermissions
614+
* @psalm-param ?non-negative-int $lobbyTimer
615+
* @param 0|1|2|null $sipEnabled Whether SIP dial-in shall be enabled (only available with `conversation-creation-all` capability)
616+
* @psalm-param ?Webinary::SIP_* $sipEnabled
617+
* @param int<0, 255>|null $permissions Default permissions for participants (only available with `conversation-creation-all` capability)
618+
* @psalm-param ?int-mask-of<Attendee::PERMISSIONS_*> $permissions
619+
* @param 0|1|null $recordingConsent Whether participants need to agree to a recording before joining a call (only available with `conversation-creation-all` capability)
620+
* @psalm-param RecordingService::CONSENT_REQUIRED_NO|RecordingService::CONSENT_REQUIRED_YES|null $recordingConsent
621+
* @param 0|1|null $mentionPermissions Who can mention at-all in the chat (only available with `conversation-creation-all` capability)
622+
* @psalm-param ?Room::MENTION_PERMISSIONS_* $mentionPermissions
619623
* @param string $description Description for the conversation (limited to 2.000 characters) (only available with `conversation-creation-all` capability)
620624
* @param ?non-empty-string $emoji Emoji for the avatar of the conversation (only available with `conversation-creation-all` capability)
621625
* @param ?non-empty-string $avatarColor Background color of the avatar (Only considered when an emoji was provided) (only available with `conversation-creation-all` capability)
@@ -635,26 +639,27 @@ protected function formatRoom(
635639
'apiVersion' => '(v4)',
636640
])]
637641
public function createRoom(
638-
int $roomType = Room::TYPE_GROUP,
642+
?int $roomType = null,
639643
string $invite = '', /* @deprecated */
640644
string $roomName = '',
641645
string $source = '', /* @deprecated */
642646
string $objectType = '',
643647
string $objectId = '',
644648
string $password = '',
645-
int $readOnly = Room::READ_WRITE,
646-
int $listable = Room::LISTABLE_NONE,
647-
int $messageExpiration = 0,
648-
int $lobbyState = Webinary::LOBBY_NONE,
649+
?int $readOnly = null,
650+
?int $listable = null,
651+
?int $messageExpiration = null,
652+
?int $lobbyState = null,
649653
?int $lobbyTimer = null,
650-
int $sipEnabled = Webinary::SIP_DISABLED,
651-
int $permissions = Attendee::PERMISSIONS_DEFAULT,
652-
int $recordingConsent = RecordingService::CONSENT_REQUIRED_NO,
653-
int $mentionPermissions = Room::MENTION_PERMISSIONS_EVERYONE,
654+
?int $sipEnabled = null,
655+
?int $permissions = null,
656+
?int $recordingConsent = null,
657+
?int $mentionPermissions = null,
654658
string $description = '',
655659
?string $emoji = null,
656660
?string $avatarColor = null,
657661
array $participants = [],
662+
?int $preset = null,
658663
): DataResponse {
659664
if ($roomType === Room::TYPE_ONE_TO_ONE) {
660665
if ($invite === ''
@@ -744,23 +749,26 @@ public function createRoom(
744749
$objectId = Room::OBJECT_ID_PHONE_OUTGOING;
745750
}
746751

752+
$selectedPreset = Preset::from($preset ?? 0);
753+
754+
747755
try {
748756
$room = $this->roomService->createConversation(
749-
$roomType,
757+
$this->presetService->getDefaultForPreset($selectedPreset, Parameter::ROOM_TYPE, $roomType),
750758
$roomName,
751759
$user,
752760
$objectType,
753761
$objectId,
754762
$password,
755-
$readOnly,
756-
$listable,
757-
$messageExpiration,
758-
$lobbyState,
763+
$this->presetService->getDefaultForPreset($selectedPreset, Parameter::READ_ONLY, $readOnly),
764+
$this->presetService->getDefaultForPreset($selectedPreset, Parameter::LISTABLE, $listable),
765+
$this->presetService->getDefaultForPreset($selectedPreset, Parameter::MESSAGE_EXPIRATION, $messageExpiration),
766+
$this->presetService->getDefaultForPreset($selectedPreset, Parameter::LOBBY_STATE, $lobbyState),
759767
$lobbyTimer,
760-
$sipEnabled,
761-
$permissions,
762-
$recordingConsent,
763-
$mentionPermissions,
768+
$this->presetService->getDefaultForPreset($selectedPreset, Parameter::SIP_ENABLED, $sipEnabled),
769+
$this->presetService->getDefaultForPreset($selectedPreset, Parameter::PERMISSIONS, $permissions),
770+
$this->presetService->getDefaultForPreset($selectedPreset, Parameter::RECORDING_CONSENT, $recordingConsent),
771+
$this->presetService->getDefaultForPreset($selectedPreset, Parameter::MENTION_PERMISSIONS, $mentionPermissions),
764772
$description,
765773
$emoji,
766774
$avatarColor,

lib/RoomPresets/DefaultPreset.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Talk\RoomPresets;
10+
11+
use OCA\Talk\Model\Attendee;
12+
use OCA\Talk\Room;
13+
use OCA\Talk\Service\RecordingService;
14+
use OCA\Talk\Webinary;
15+
16+
class DefaultPreset implements IPreset {
17+
public static function getDefault(Parameter $parameter): int {
18+
return match ($parameter) {
19+
Parameter::ROOM_TYPE => Room::TYPE_GROUP,
20+
Parameter::READ_ONLY => Room::READ_WRITE,
21+
Parameter::LISTABLE => Room::LISTABLE_NONE,
22+
Parameter::MESSAGE_EXPIRATION => 0,
23+
Parameter::LOBBY_STATE => Webinary::LOBBY_NONE,
24+
Parameter::SIP_ENABLED => Webinary::SIP_DISABLED,
25+
Parameter::PERMISSIONS => Attendee::PERMISSIONS_DEFAULT,
26+
Parameter::RECORDING_CONSENT => RecordingService::CONSENT_REQUIRED_NO,
27+
Parameter::MENTION_PERMISSIONS => Room::MENTION_PERMISSIONS_EVERYONE,
28+
};
29+
}
30+
}

lib/RoomPresets/Hallway.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Talk\RoomPresets;
10+
11+
use OCA\Talk\Room;
12+
13+
class Hallway implements IPreset {
14+
public static function getDefault(Parameter $parameter): ?int {
15+
return match ($parameter) {
16+
// Users but no guest users (by default)
17+
Parameter::LISTABLE => Room::LISTABLE_USERS,
18+
// If you were not there, you were not there …
19+
Parameter::MESSAGE_EXPIRATION => 3600,
20+
default => null,
21+
};
22+
}
23+
}

lib/RoomPresets/IPreset.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Talk\RoomPresets;
10+
11+
interface IPreset {
12+
public static function getDefault(Parameter $parameter): ?int;
13+
}

lib/RoomPresets/Parameter.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Talk\RoomPresets;
10+
11+
12+
enum Parameter: string
13+
{
14+
case ROOM_TYPE = 'roomType';
15+
case READ_ONLY = 'readOnly';
16+
case LISTABLE = 'listable';
17+
case MESSAGE_EXPIRATION = 'messageExpiration';
18+
case LOBBY_STATE = 'lobbyState';
19+
case SIP_ENABLED = 'sipEnabled';
20+
case PERMISSIONS = 'permissions';
21+
case RECORDING_CONSENT = 'recordingConsent';
22+
case MENTION_PERMISSIONS = 'mentionPermissions';
23+
}

lib/RoomPresets/Presentation.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Talk\RoomPresets;
10+
11+
use OCA\Talk\Model\Attendee;
12+
use OCA\Talk\Room;
13+
use OCA\Talk\Service\RecordingService;
14+
15+
class Presentation implements IPreset {
16+
public static function getDefault(Parameter $parameter): ?int {
17+
return match ($parameter) {
18+
Parameter::MENTION_PERMISSIONS => Room::MENTION_PERMISSIONS_MODERATORS,
19+
Parameter::PERMISSIONS => Attendee::PERMISSIONS_CUSTOM
20+
| Attendee::PERMISSIONS_CALL_JOIN
21+
| Attendee::PERMISSIONS_CHAT,
22+
Parameter::RECORDING_CONSENT => RecordingService::CONSENT_REQUIRED_YES,
23+
default => null,
24+
};
25+
}
26+
}

lib/RoomPresets/Preset.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Talk\RoomPresets;
10+
11+
12+
enum Preset: int
13+
{
14+
case DEFAULT = 0;
15+
case WEBINAR = 1;
16+
case PRESENTATION = 2;
17+
case HALLWAY = 3;
18+
}

lib/RoomPresets/Webinar.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Talk\RoomPresets;
10+
11+
use OCA\Talk\Model\Attendee;
12+
use OCA\Talk\Room;
13+
use OCA\Talk\Service\RecordingService;
14+
use OCA\Talk\Webinary;
15+
16+
class Webinar implements IPreset {
17+
public static function getDefault(Parameter $parameter): ?int {
18+
return match ($parameter) {
19+
Parameter::LOBBY_STATE => Webinary::LOBBY_NON_MODERATORS,
20+
Parameter::MESSAGE_EXPIRATION => Room::MENTION_PERMISSIONS_MODERATORS,
21+
Parameter::PERMISSIONS => Attendee::PERMISSIONS_CUSTOM
22+
| Attendee::PERMISSIONS_CALL_JOIN
23+
| Attendee::PERMISSIONS_CHAT,
24+
Parameter::RECORDING_CONSENT => RecordingService::CONSENT_REQUIRED_YES,
25+
Parameter::ROOM_TYPE => Room::TYPE_PUBLIC,
26+
default => null,
27+
};
28+
}
29+
}

lib/Service/RoomPresetService.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Talk\Service;
10+
11+
use OCA\Talk\RoomPresets\DefaultPreset;
12+
use OCA\Talk\RoomPresets\Hallway;
13+
use OCA\Talk\RoomPresets\Parameter;
14+
use OCA\Talk\RoomPresets\Presentation;
15+
use OCA\Talk\RoomPresets\Preset;
16+
use OCA\Talk\RoomPresets\Webinar;
17+
use OCP\AppFramework\Services\IAppConfig;
18+
19+
class RoomPresetService {
20+
public const CONFIG_PREFIX_DEFAULT = 'default_';
21+
public const CONFIG_PREFIX_FORCE = 'force_';
22+
23+
public function __construct(
24+
protected readonly IAppConfig $appConfig,
25+
) {
26+
}
27+
28+
/**
29+
* Order of priority:
30+
* 1. App config `force_`
31+
* 2. User provided value
32+
* 3. Preset value
33+
* 4. App config `default_`
34+
* 5. Source code default
35+
*/
36+
public function getDefaultForPreset(Preset $preset, Parameter $parameter, ?int $provided): int {
37+
$configName = self::getConfigNameForParameter($parameter);
38+
if ($this->appConfig->hasAppKey(self::CONFIG_PREFIX_FORCE . $configName)) {
39+
return $this->appConfig->getAppValueInt(self::CONFIG_PREFIX_FORCE . $configName);
40+
}
41+
42+
if ($provided !== null) {
43+
return $provided;
44+
}
45+
46+
$value = match ($preset) {
47+
Preset::WEBINAR => Webinar::getDefault($parameter),
48+
Preset::PRESENTATION => Presentation::getDefault($parameter),
49+
Preset::HALLWAY => Hallway::getDefault($parameter),
50+
default => null,
51+
};
52+
53+
if ($value !== null) {
54+
return $value;
55+
}
56+
57+
if ($this->appConfig->hasAppKey(self::CONFIG_PREFIX_DEFAULT . $configName)) {
58+
return $this->appConfig->getAppValueInt(self::CONFIG_PREFIX_DEFAULT . $configName);
59+
}
60+
61+
// Fall through to default preset
62+
return DefaultPreset::getDefault($parameter);
63+
}
64+
65+
public static function getConfigNameForParameter(Parameter $parameter): string {
66+
$parts = preg_split('/(?=[A-Z])/', $parameter->value);
67+
68+
$configName = '';
69+
foreach ($parts as $part) {
70+
if ($configName === '') {
71+
$configName = $part;
72+
} else {
73+
$configName .= '_' . lcfirst($part);
74+
}
75+
}
76+
77+
return $configName;
78+
}
79+
}

0 commit comments

Comments
 (0)