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 @@
+
+