diff --git a/app/Http/Controllers/api/v1/RoomController.php b/app/Http/Controllers/api/v1/RoomController.php index ec3c598a4..413a206c3 100644 --- a/app/Http/Controllers/api/v1/RoomController.php +++ b/app/Http/Controllers/api/v1/RoomController.php @@ -174,6 +174,11 @@ public function store(CreateRoom $request) $room->access_code = random_int(111111111, 999999999); } + // Create dialin pin if activated for this room type + if ($room->roomType->dialin_pin_default) { + $room->dialin_pin = random_int(11111, 99999); + } + // Apply non-expert settings of the room type foreach (Room::ROOM_SETTINGS_DEFINITION as $setting => $config) { if (! $config['expert']) { @@ -273,6 +278,7 @@ public function update(UpdateRoomSettings $request, Room $room) $room->expert_mode = $request->expert_mode; $room->short_description = $request->short_description; $room->access_code = $request->access_code; + $room->dialin_pin = $request->dialin_pin; foreach (Room::ROOM_SETTINGS_DEFINITION as $setting => $config) { // Expert mode for room is deactivated and setting is an expert setting: do not update setting diff --git a/app/Http/Controllers/api/v1/RoomTypeController.php b/app/Http/Controllers/api/v1/RoomTypeController.php index cefb74241..d08c9f5c8 100644 --- a/app/Http/Controllers/api/v1/RoomTypeController.php +++ b/app/Http/Controllers/api/v1/RoomTypeController.php @@ -108,6 +108,8 @@ public function update(RoomTypeRequest $request, RoomType $roomType) $roomType->has_access_code_default = $request->has_access_code_default; $roomType->has_access_code_enforced = $request->has_access_code_enforced; + $roomType->has_dialin_pin_default = $request->has_dialin_pin_default; + $roomType->has_dialin_pin_enforced = $request->has_dialin_pin_enforced; $roomType->save(); @@ -143,6 +145,8 @@ public function store(RoomTypeRequest $request) $roomType->has_access_code_default = $request->has_access_code_default; $roomType->has_access_code_enforced = $request->has_access_code_enforced; + $roomType->has_dialin_pin_default = $request->has_dialin_pin_default; + $roomType->has_dialin_pin_enforced = $request->has_dialin_pin_enforced; $roomType->save(); diff --git a/app/Http/Requests/RoomTypeRequest.php b/app/Http/Requests/RoomTypeRequest.php index 023a9299c..68460baf6 100644 --- a/app/Http/Requests/RoomTypeRequest.php +++ b/app/Http/Requests/RoomTypeRequest.php @@ -25,6 +25,8 @@ public function rules() // Default room settings 'has_access_code_default' => ['required', 'boolean'], 'has_access_code_enforced' => ['required', 'boolean'], + 'has_dialin_pin_default' => ['required', 'boolean'], + 'has_dialin_pin_enforced' => ['required', 'boolean'], ]; // Default room settings @@ -50,6 +52,8 @@ public function attributes(): array $locales = [ 'has_access_code_default' => __('validation.room_type_attribute_default', ['attribute' => __('validation.attributes.has_access_code')]), 'has_access_code_enforced' => __('validation.room_type_attribute_enforced', ['attribute' => __('validation.attributes.has_access_code')]), + 'has_dialin_pin_default' => __('validation.room_type_attribute_default', ['attribute' => __('validation.attributes.has_dialin_pin')]), + 'has_dialin_pin_enforced' => __('validation.room_type_attribute_enforced', ['attribute' => __('validation.attributes.has_dialin_pin')]), ]; foreach (Room::ROOM_SETTINGS_DEFINITION as $setting => $config) { diff --git a/app/Http/Requests/UpdateRoomSettings.php b/app/Http/Requests/UpdateRoomSettings.php index 1dee65f68..7692303d5 100644 --- a/app/Http/Requests/UpdateRoomSettings.php +++ b/app/Http/Requests/UpdateRoomSettings.php @@ -17,6 +17,7 @@ public function rules() 'name' => ['required', 'string', 'min:2', 'max:'.config('bigbluebutton.room_name_limit')], 'short_description' => ['nullable', 'string', 'max:300'], 'expert_mode' => ['required', 'boolean'], + 'dialin_pin' => $this->getDialinPinValidationRule(), ]; // Generate validation rules for all visible room settings @@ -66,4 +67,36 @@ private function getAccessCodeValidationRule(): array return $rules; } + + /** + * Set dialin_pin validation rule based on the settings in the room type + * + * @return string[] dialin_pin validation rules + */ + private function getDialinPinValidationRule(): array + { + $rules = ['integer', 'digits:5', 'bail', 'min:10000', 'max:99999', 'unique:rooms,dialin_pin,' . $this->room->id]; + + // Make sure that the given room type id is a number + if (is_numeric($this->input('room_type'))) { + // Check if a room type exists with the given number + $newRoomType = RoomType::find($this->input('room_type')); + if ($newRoomType) { + // Set dialin_pin to required if enforced in room type + if ($newRoomType->has_dialin_pin_enforced && $newRoomType->has_dialin_pin_default) { + array_unshift($rules, 'required'); + } + // Set dialin_pin to prohibited if enforced in room type + elseif ($newRoomType->has_dialin_pin_enforced && ! $newRoomType->has_dialin_pin_default) { + array_unshift($rules, 'prohibited', 'nullable'); + } + // Set dialin_pin to nullable (room can have an dialin_pin but dialin_pin is not enforced) + else { + array_unshift($rules, 'nullable'); + } + } + } + + return $rules; + } } diff --git a/app/Http/Resources/RoomSettings.php b/app/Http/Resources/RoomSettings.php index 4429d6df6..9c7ec7ba1 100644 --- a/app/Http/Resources/RoomSettings.php +++ b/app/Http/Resources/RoomSettings.php @@ -31,6 +31,7 @@ public function toArray($request) 'welcome' => $this->expert_mode ? $this->welcome : '', 'short_description' => $this->short_description, 'access_code' => $this->access_code, + 'dialin_pin' => $this->dialin_pin, 'room_type' => (new RoomType($this->roomType))->withDefaultRoomSettings()->withFeatures(), $this->merge($this->getRoomSettings()), ]; diff --git a/app/Http/Resources/RoomType.php b/app/Http/Resources/RoomType.php index fc5cb43fc..7e1c25cdc 100644 --- a/app/Http/Resources/RoomType.php +++ b/app/Http/Resources/RoomType.php @@ -73,6 +73,9 @@ public function getDefaultRoomSettings() $settings['has_access_code_default'] = $this->has_access_code_default; $settings['has_access_code_enforced'] = $this->has_access_code_enforced; + $settings['has_dialin_pin_default'] = $this->has_dialin_pin_default; + $settings['has_dialin_pin_enforced'] = $this->has_dialin_pin_enforced; + return $settings; } diff --git a/app/Models/Room.php b/app/Models/Room.php index 181d29ab7..eda2d7bb9 100644 --- a/app/Models/Room.php +++ b/app/Models/Room.php @@ -66,6 +66,7 @@ protected function casts() 'expert_mode' => 'boolean', 'delete_inactive' => 'datetime', 'access_code' => 'integer', + 'dialin_pin' => 'integer', ]; // Generate casts for settings that are also present in the room type diff --git a/app/Models/RoomType.php b/app/Models/RoomType.php index 9d11369ab..e83131b94 100644 --- a/app/Models/RoomType.php +++ b/app/Models/RoomType.php @@ -21,6 +21,8 @@ protected function casts() // Default room settings 'has_access_code_default' => 'boolean', 'has_access_code_enforced' => 'boolean', + 'has_dialin_pin_default' => 'boolean', + 'has_dialin_pin_enforced' => 'boolean', ]; // Generate casts for default room settings (that are also present in the room) diff --git a/app/Services/MeetingService.php b/app/Services/MeetingService.php index 56c89cc9e..5386411d9 100644 --- a/app/Services/MeetingService.php +++ b/app/Services/MeetingService.php @@ -98,7 +98,8 @@ public function start(): ?\BigBlueButton\Responses\CreateMeetingResponse ->setLockSettingsDisableNotes($this->meeting->room->getRoomSetting('lock_settings_disable_note')) ->setLockSettingsHideUserList($this->meeting->room->getRoomSetting('lock_settings_hide_user_list')) ->setLockSettingsLockOnJoin(true) - ->setMuteOnStart($this->meeting->room->getRoomSetting('mute_on_start')); + ->setMuteOnStart($this->meeting->room->getRoomSetting('mute_on_start')) + ->setVoiceBridge($this->meeting->room->getRoomSetting('dialin_pin')); $meetingParams->addMeta('bbb-origin', 'PILOS'); $meetingParams->addMeta('pilos-sub-spool-dir', config('recording.spool-sub-directory')); diff --git a/database/migrations/2025_05_05_222245_add_dialin_pin_to_rooms_table.php b/database/migrations/2025_05_05_222245_add_dialin_pin_to_rooms_table.php new file mode 100644 index 000000000..907a60fc4 --- /dev/null +++ b/database/migrations/2025_05_05_222245_add_dialin_pin_to_rooms_table.php @@ -0,0 +1,38 @@ +boolean('has_dialin_pin_enforced')->after('has_access_code_default')->default(false); + $table->boolean('has_dialin_pin_default')->after('has_dialin_pin_enforced')->default(false); + }); + Schema::table('rooms', function (Blueprint $table) { + $table->integer('dialin_pin')->nullable()->unique()->after('meeting_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('room_types', function (Blueprint $table) { + $table->dropColumn('has_dialin_pin_enforced'); + $table->dropColumn('has_dialin_pin_default'); + }); + Schema::table('rooms', function (Blueprint $table) { + $table->dropColumn('dialin_pin'); + }); + } +}; diff --git a/lang/de/rooms.php b/lang/de/rooms.php index f6fd4a9d6..78560560a 100644 --- a/lang/de/rooms.php +++ b/lang/de/rooms.php @@ -2,6 +2,7 @@ return [ 'access_code' => 'Zugangscode', + 'dialin_pin' => 'Einwahl-PIN', 'auth_throttled' => 'Zu viele Versuche. Bitte versuchen Sie es in :try_again Sekunden erneut.', 'become_member' => 'Mitglied werden', 'change_type' => [ @@ -358,6 +359,12 @@ 'public' => 'Öffentlich', 'title' => 'Sichtbarkeit', ], + 'has_dialin_pin' => 'Einwahl PIN', + 'dialin_pin_none_placeholder' => '-- Zufällig --', + 'dialin_pin_enforced' => 'Die Raumart erzwingt, dass ein Einwahl Pin existiert', + 'dialin_pin_prohibited' => 'Die Raumart erzwingt, dass kein Einwahl Pin existiert', + 'generate_dialin_pin' => 'Neuen Einwahl Pin erstellen', + 'delete_dialin_pin' => 'Einwahl Pin entfernen', ], 'expert_mode' => [ 'activate' => 'Expertenmodus aktivieren', diff --git a/lang/de/validation.php b/lang/de/validation.php index 166c611b4..576074b26 100644 --- a/lang/de/validation.php +++ b/lang/de/validation.php @@ -80,6 +80,7 @@ 'general_toast_lifetime' => 'Anzeigedauer von Pop-up-Nachrichten', 'generate_password' => 'Passwort generieren lassen', 'has_access_code' => 'Zugangscode', + 'has_dialin_pin' => 'Einwahl PIN', 'hour' => 'Stunde', 'image' => 'Bild', 'last_name' => 'Nachname', @@ -164,6 +165,7 @@ 'username' => 'Benutzerkennung', 'users' => 'Benutzer', 'visibility' => 'Sichtbarkeit', + 'dialin_pin' => 'Einwahl PIN', 'webcams_only_for_moderator' => 'Webcam nur für Moderatoren sichtbar', 'welcome' => 'Begrüßungsnachricht', 'year' => 'Jahr', diff --git a/lang/en/rooms.php b/lang/en/rooms.php index 1c0ffc042..9ecf02a73 100644 --- a/lang/en/rooms.php +++ b/lang/en/rooms.php @@ -2,6 +2,7 @@ return [ 'access_code' => 'Access code', + 'dialin_pin' => 'Dialin PIN', 'auth_throttled' => 'Too many attempts. Please try again in :try_again seconds.', 'become_member' => 'Become member', 'change_type' => [ @@ -358,6 +359,12 @@ 'public' => 'Public', 'title' => 'Visibility', ], + 'has_dialin_pin' => 'Dialin PIN', + 'dialin_pin_none_placeholder' => '-- Random --', + 'dialin_pin_enforced' => 'The room type enforces the existence of an dialin pin', + 'dialin_pin_prohibited' => 'The room type enforces the absence of an dialin pin', + 'generate_dialin_pin' => 'Generate new dialin pin', + 'delete_dialin_pin' => 'Delete dialin pin', ], 'expert_mode' => [ 'activate' => 'Activate expert mode', diff --git a/lang/en/validation.php b/lang/en/validation.php index 1f3d75efb..1588f9e8d 100644 --- a/lang/en/validation.php +++ b/lang/en/validation.php @@ -80,6 +80,7 @@ 'general_toast_lifetime' => 'Display duration of pop-up messages', 'generate_password' => 'Generate password', 'has_access_code' => 'Access code', + 'has_dialin_pin' => 'Dialin PIN', 'hour' => 'Hour', 'image' => 'Image', 'last_name' => 'Last name', @@ -164,6 +165,7 @@ 'username' => 'Username', 'users' => 'Users', 'visibility' => 'Visibility', + 'dialin_pin' => 'Dialin PIN', 'webcams_only_for_moderator' => 'Only moderators can see the webcam', 'welcome' => 'Welcome message', 'year' => 'Year', diff --git a/resources/js/components/RoomTabSettings.vue b/resources/js/components/RoomTabSettings.vue index 99f48746d..e04ebf492 100644 --- a/resources/js/components/RoomTabSettings.vue +++ b/resources/js/components/RoomTabSettings.vue @@ -129,6 +129,7 @@ import RoomTabSettingsRadioGroup from "./RoomTabSettingsRadioGroup.vue"; import RoomTabSettingsSelectButton from "./RoomTabSettingsSelectButton.vue"; import RoomTabSettingsRoomTypeSelect from "./RoomTabSettingsRoomTypeSelect.vue"; import RoomTabSettingsAccessCodeInput from "./RoomTabSettingsAccessCodeInput.vue"; +import RoomTabSettingsDialinPinInput from "./RoomTabSettingsDialinPinInput.vue"; const props = defineProps({ room: { @@ -334,6 +335,12 @@ const form = computed(() => { { value: 1, label: t("rooms.settings.advanced.visibility.public") }, ], }, + { + setting: "dialin_pin", + label: t("rooms.settings.advanced.has_dialin_pin"), + placeholder: t("rooms.settings.advanced.dialin_pin_none_placeholder"), + component: RoomTabSettingsDialinPinInput, + } ], }, ]; diff --git a/resources/js/components/RoomTabSettingsDialinPinInput.vue b/resources/js/components/RoomTabSettingsDialinPinInput.vue new file mode 100644 index 000000000..d442ab03d --- /dev/null +++ b/resources/js/components/RoomTabSettingsDialinPinInput.vue @@ -0,0 +1,113 @@ + + + diff --git a/resources/js/composables/useRoomTypeSettings.js b/resources/js/composables/useRoomTypeSettings.js index 493341e1b..cfacfd5d6 100644 --- a/resources/js/composables/useRoomTypeSettings.js +++ b/resources/js/composables/useRoomTypeSettings.js @@ -145,6 +145,12 @@ export function useRoomTypeSettings() { 1: t("rooms.settings.advanced.visibility.public"), }, }, + { + key: "dialin_pin", + current_value_key: "dialin_pin", + label: t("rooms.settings.advanced.dialin_pin"), + type: "switch", + }, ], }, ]; diff --git a/resources/js/constants/roomSettings.js b/resources/js/constants/roomSettings.js index 9a035eecc..10a1c9354 100644 --- a/resources/js/constants/roomSettings.js +++ b/resources/js/constants/roomSettings.js @@ -50,6 +50,9 @@ export const ROOM_SETTINGS_DEFINITION = { visibility: { expert_setting: true, }, + dialin_pin: { + expert_setting: true, + }, welcome: { expert_setting: true, has_no_room_type_default: true, diff --git a/resources/js/views/AdminRoomTypesView.vue b/resources/js/views/AdminRoomTypesView.vue index 07f51aac7..e92f42ccd 100644 --- a/resources/js/views/AdminRoomTypesView.vue +++ b/resources/js/views/AdminRoomTypesView.vue @@ -1403,6 +1403,52 @@ + + +
+ +
+
+ + +
+
+ + +
+
+
@@ -1561,6 +1607,8 @@ const model = ref({ visibility_enforced: false, has_access_code_default: true, has_access_code_enforced: false, + has_dialin_pin_default: false, + has_dialin_pin_enforced: false, }); const name = ref("");