diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f13e0ae1..cfc20b094 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Added
+
+- Option in room file management to always use system-wide default presentation ([#2662], [#2746])
+- Option in room file management to set system-wide default presentation as default ([#2662], [#2746])
+
### Changed
- Internal improvements to room authentication flow ([#1409], [#2726])
@@ -674,10 +679,12 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#2613]: https://github.com/THM-Health/PILOS/pull/2613
[#2616]: https://github.com/THM-Health/PILOS/pull/2616
[#2660]: https://github.com/THM-Health/PILOS/issues/2660
+[#2662]: https://github.com/THM-Health/PILOS/issues/2662
[#2686]: https://github.com/THM-Health/PILOS/pull/2686
[#2726]: https://github.com/THM-Health/PILOS/pull/2726
[#2728]: https://github.com/THM-Health/PILOS/pull/2728
[#2742]: https://github.com/THM-Health/PILOS/pull/2742
+[#2746]: https://github.com/THM-Health/PILOS/pull/2746
[#2751]: https://github.com/THM-Health/PILOS/issues/2751
[#2752]: https://github.com/THM-Health/PILOS/pull/2752
[#2765]: https://github.com/THM-Health/PILOS/issues/2765
diff --git a/app/Http/Controllers/api/v1/RoomFileController.php b/app/Http/Controllers/api/v1/RoomFileController.php
index 2d45bcbf8..dd6500e58 100644
--- a/app/Http/Controllers/api/v1/RoomFileController.php
+++ b/app/Http/Controllers/api/v1/RoomFileController.php
@@ -5,9 +5,11 @@
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreRoomFile;
use App\Http\Requests\UpdateRoomFile;
+use App\Http\Requests\UpdateRoomSystemDefaultPresentation;
use App\Http\Resources\PrivateRoomFile;
use App\Models\Room;
use App\Models\RoomFile;
+use App\Settings\BigBlueButtonSettings;
use App\Settings\GeneralSettings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
@@ -67,6 +69,11 @@ public function index(Room $room, Request $request)
// If user is allowed to view all files, return PrivateRoomFile resource to show additional information
if (Gate::allows('viewAllFiles', $room)) {
$additional['default'] = $room->files()->where('default', true)->first();
+ $additional['system_default'] = [
+ 'file' => app(BigBlueButtonSettings::class)->default_presentation,
+ 'use_in_meeting' => $room->use_system_default_presentation_in_meeting,
+ 'use_as_default' => $room->use_system_default_presentation_as_default,
+ ];
return PrivateRoomFile::collection($resource->paginate(app(GeneralSettings::class)->pagination_page_size))->additional($additional);
}
@@ -103,22 +110,26 @@ public function store(Room $room, StoreRoomFile $request)
public function update(UpdateRoomFile $request, Room $room, RoomFile $file)
{
if ($request->has('use_in_meeting')) {
- $file->use_in_meeting = $request->use_in_meeting;
- // If no default file for this room is set, set this file as default
- if (! $room->files()->where('default', true)->exists()) {
- $file->default = true;
- }
+ $file->use_in_meeting = $request->boolean('use_in_meeting');
}
if ($request->has('download')) {
- $file->download = $request->download;
+ $file->download = $request->boolean('download');
}
- if ($request->has('default') && $request->default === true) {
- // Make other files not the default
- $room->files()->update(['default' => false]);
- // Set this file as default
- $file->default = true;
+ if ($request->has('default')) {
+ if ($request->boolean('default') === false) {
+ $file->default = false;
+ } else {
+ // Make other files not the default
+ $room->files()->whereNot('id', $file->id)->update(['default' => false]);
+ // Set this file as default
+ $file->default = true;
+
+ // If a file is set as default, the system default presentation must not be used as default
+ $room->use_system_default_presentation_as_default = false;
+ $room->save();
+ }
}
$file->save();
@@ -130,6 +141,33 @@ public function update(UpdateRoomFile $request, Room $room, RoomFile $file)
return response()->noContent();
}
+ public function updateSystemDefault(UpdateRoomSystemDefaultPresentation $request, Room $room)
+ {
+ if ($request->has('use_in_meeting')) {
+ $room->use_system_default_presentation_in_meeting = $request->use_in_meeting;
+ }
+
+ if ($request->has('default')) {
+ // Make other files not the default
+ if ($request->default === true) {
+ $room->files()->update(['default' => false]);
+
+ // If system default is set as default, it must also be used in the next meeting
+ $room->use_system_default_presentation_in_meeting = true;
+ }
+
+ $room->use_system_default_presentation_as_default = $request->default;
+ }
+
+ $room->save();
+
+ Log::info('Changed system default presentation settings in room {room}', ['room' => $room->getLogLabel()]);
+
+ $room->updateDefaultFile();
+
+ return response()->noContent();
+ }
+
/**
* Remove the specified file from storage and database.
*
diff --git a/app/Http/Requests/UpdateRoomSystemDefaultPresentation.php b/app/Http/Requests/UpdateRoomSystemDefaultPresentation.php
new file mode 100644
index 000000000..7fa8c5917
--- /dev/null
+++ b/app/Http/Requests/UpdateRoomSystemDefaultPresentation.php
@@ -0,0 +1,16 @@
+ ['required', 'boolean'],
+ 'default' => ['required', 'boolean'],
+ ];
+ }
+}
diff --git a/app/Models/Room.php b/app/Models/Room.php
index 70591c156..fecac4533 100644
--- a/app/Models/Room.php
+++ b/app/Models/Room.php
@@ -6,6 +6,7 @@
use App\Enums\RoomUserRole;
use App\Enums\RoomVisibility;
use App\Observers\RoomObserver;
+use App\Settings\BigBlueButtonSettings;
use App\Settings\GeneralSettings;
use App\Traits\AddsModelNameTrait;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
@@ -28,6 +29,8 @@ protected function casts()
{
$casts = [
'expert_mode' => 'boolean',
+ 'use_system_default_presentation_in_meeting' => 'boolean',
+ 'use_system_default_presentation_as_default' => 'boolean',
'delete_inactive' => 'datetime',
];
@@ -201,7 +204,18 @@ public function updateDefaultFile()
$currentDefault->default = false;
$currentDefault->save();
}
- // If any other files are found that are used in the next meeting, select the first one to become new default
+
+ // If system has a default presentation and the system presentation is always available
+ // use system default presentation as default presentation for the next meeting
+ if (app(BigBlueButtonSettings::class)->default_presentation && $this->use_system_default_presentation_in_meeting) {
+ $this->use_system_default_presentation_as_default = true;
+ $this->save();
+
+ return;
+ }
+
+ // If no default file is explicitly set or the system default is not enabled
+ // look for the first file that is set to be used in the meeting and set it as default
$newDefaultFile = $this->files()->firstWhere('use_in_meeting', true);
if ($newDefaultFile != null) {
$newDefaultFile->default = true;
diff --git a/app/Services/MeetingService.php b/app/Services/MeetingService.php
index 5bccd3edb..3b65fe8f0 100644
--- a/app/Services/MeetingService.php
+++ b/app/Services/MeetingService.php
@@ -104,7 +104,13 @@ public function start(): ?\BigBlueButton\Responses\CreateMeetingResponse
$meetingParams->addMeta('bbb-origin', 'PILOS');
$meetingParams->addMeta('pilos-sub-spool-dir', config('recording.spool-sub-directory'));
- // get files that should be used in this meeting and add links to the files
+ // Use system default presentation as default, if explicitly set
+ $useSystemDefaultFileAsDefault = app(BigBlueButtonSettings::class)->default_presentation && $this->meeting->room->use_system_default_presentation_as_default;
+ if ($useSystemDefaultFileAsDefault) {
+ $meetingParams->addPresentation(app(BigBlueButtonSettings::class)->default_presentation);
+ }
+
+ // Get files that should be used in this meeting and add links to the files
$files = $this->meeting->room->files()->where('use_in_meeting', true)->orderBy('default', 'desc')->get();
foreach ($files as $file) {
// Create file download url
@@ -117,7 +123,12 @@ public function start(): ?\BigBlueButton\Responses\CreateMeetingResponse
$meetingParams->addPresentation($fileUrl, null, preg_replace("/[^A-Za-z0-9.-_\(\)]/", '', $file->filename));
}
- if (empty($meetingParams->getPresentations()) && app(BigBlueButtonSettings::class)->default_presentation) {
+ // Add system default presentation
+ // Only add it, if not already added as default file
+ // and if no other files are present or the room is set to use the system default in meetings
+ if (! $useSystemDefaultFileAsDefault && app(BigBlueButtonSettings::class)->default_presentation && (
+ empty($files->toArray()) || $this->meeting->room->use_system_default_presentation_in_meeting
+ )) {
$meetingParams->addPresentation(app(BigBlueButtonSettings::class)->default_presentation);
}
diff --git a/database/migrations/2026_01_19_105554_add_use_system_default_presentation_in_next_meeting_to_rooms_table.php b/database/migrations/2026_01_19_105554_add_use_system_default_presentation_in_next_meeting_to_rooms_table.php
new file mode 100644
index 000000000..0898003e7
--- /dev/null
+++ b/database/migrations/2026_01_19_105554_add_use_system_default_presentation_in_next_meeting_to_rooms_table.php
@@ -0,0 +1,39 @@
+boolean('use_system_default_presentation_in_meeting')->default(true);
+ $table->boolean('use_system_default_presentation_as_default')->default(true);
+ });
+
+ RoomFile::where('default', true)
+ ->where('use_in_meeting', true)
+ ->each(function (RoomFile $file) {
+ $file->room->use_system_default_presentation_as_default = false;
+ $file->room->save();
+ });
+
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('rooms', function (Blueprint $table) {
+ $table->dropColumn('use_system_default_presentation_in_meeting');
+ $table->dropColumn('use_system_default_presentation_as_default');
+ });
+ }
+};
diff --git a/lang/en/rooms.php b/lang/en/rooms.php
index d09571019..762240725 100644
--- a/lang/en/rooms.php
+++ b/lang/en/rooms.php
@@ -107,12 +107,14 @@
'feature_disabled_system' => ':name is disabled. Please contact the administrator.',
'files' => [
'confirm_delete' => 'Do you want to delete this file :filename?',
- 'default' => 'Default',
+ 'default' => 'Default presentation',
'delete' => 'Delete file',
- 'download_hidden' => 'Download hidden',
- 'download_visible' => 'Download visible',
+ 'download_allowed' => 'Download allowed',
+ 'download_not_allowed' => 'Download not allowed',
'downloadable' => 'Downloadable',
- 'edit' => 'Edit file',
+ 'configure' => 'Configure file',
+ 'configure_dialog_title' => 'Configure ":name"',
+ 'configure_system_default' => 'Configure system-wide default presentation',
'filter' => [
'all' => 'All files',
'downloadable' => 'Downloadable files',
@@ -126,6 +128,8 @@
'filename' => 'Filename',
'uploaded_at' => 'Added',
],
+ 'system_default' => 'System-wide default presentation',
+ 'system_default_description' => 'Automatically used when no other presentation is enabled for the next video conference',
'terms_of_use' => [
'accept' => 'I accept the terms of use',
'required' => 'You must accept the terms of use before downloading this file.',
@@ -134,8 +138,9 @@
'title' => 'Files',
'upload' => 'Upload files',
'uploaded' => 'File \':name\' uploaded',
- 'use_in_next_meeting' => 'Use in the next meeting',
- 'use_in_next_meeting_disabled' => 'Not available in video conference',
+ 'always_available_in_meeting' => 'Always available in next video conference',
+ 'available_in_next_meeting' => 'Available in next video conference',
+ 'not_available_in_next_meeting' => 'Not available in next video conference',
'view' => 'View file',
],
'first_and_lastname' => 'First- and last name',
diff --git a/resources/css/override/_animations.css b/resources/css/override/_animations.css
index 2930e5735..e1f316049 100644
--- a/resources/css/override/_animations.css
+++ b/resources/css/override/_animations.css
@@ -8,3 +8,8 @@
animation-duration: 0s !important;
}
}
+
+.inline-note.p-message-enter-active,
+.inline-note.p-message-leave-active {
+ animation-duration: 0s !important;
+}
diff --git a/resources/js/components/InlineNote.vue b/resources/js/components/InlineNote.vue
index 24f03ceec..43198b660 100644
--- a/resources/js/components/InlineNote.vue
+++ b/resources/js/components/InlineNote.vue
@@ -8,7 +8,11 @@ defineProps({
-
{{ item.filename }}
+
-
+
-