Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
cd96a3d
Move guest name to access code overlay
Sabr1n4W Jun 17, 2026
a4614b6
Persist access code and personalized link
Sabr1n4W Jun 17, 2026
8478aa1
Merge remote-tracking branch 'origin/develop' into refactor-flow-ente…
Sabr1n4W Jun 19, 2026
663fb83
Code cleanup
Sabr1n4W Jun 19, 2026
2d70b1f
Add legacy route for personalized room links
Sabr1n4W Jun 19, 2026
4f3e188
Merge remote-tracking branch 'origin/develop' into refactor-flow-ente…
Sabr1n4W Jun 23, 2026
c146655
Code Cleanup
Sabr1n4W Jun 23, 2026
0f5580e
Switch to alternative design
Sabr1n4W Jun 23, 2026
1deb299
Set accessCode in sessionStorage after read from hash
Sabr1n4W Jun 24, 2026
e86047d
Improve alternative design and move warning messages
Sabr1n4W Jun 24, 2026
712bac0
Start adjusting frontend tests
Sabr1n4W Jun 26, 2026
c345e7c
Start adding frontend validation for guest name
Sabr1n4W Jun 30, 2026
446d734
Clear access code item in sessionStorage after require_code errors
Sabr1n4W Jun 30, 2026
23a9edd
Improve spacing
Sabr1n4W Jun 30, 2026
0a85f89
Code cleanup
Sabr1n4W Jun 30, 2026
98724ab
Merge remote-tracking branch 'origin/develop' into refactor-flow-ente…
Sabr1n4W Jul 1, 2026
c6a23b0
Add missing frontend tests
Sabr1n4W Jul 1, 2026
89905aa
Adjust system tests
Sabr1n4W Jul 1, 2026
152a86f
Reset tab hash when falling back to default tab in tab sections
Sabr1n4W Jul 5, 2026
360e970
Merge remote-tracking branch 'origin/develop' into refactor-flow-ente…
Sabr1n4W Jul 7, 2026
2da2bf9
Move participant name validation from frontend to backend
Sabr1n4W Jul 7, 2026
368f883
Merge remote-tracking branch 'origin/develop' into refactor-flow-ente…
Sabr1n4W Jul 7, 2026
f96f113
Improve padding
Sabr1n4W Jul 7, 2026
bdb4e98
Improve room join participant name error message
Sabr1n4W Jul 7, 2026
309d0e3
Add invalid room tab hash test
Sabr1n4W Jul 7, 2026
ee51bc6
Fix cs
Sabr1n4W Jul 7, 2026
dc651df
Cleanup
Sabr1n4W Jul 8, 2026
5d72cf7
Merge remote-tracking branch 'origin/develop' into refactor-flow-ente…
Sabr1n4W Jul 8, 2026
3c0ebc4
Update changelog
Sabr1n4W Jul 8, 2026
39ec95c
Add missing intercepts
Sabr1n4W Jul 8, 2026
0095def
Move guests only check to initializeRoomView
Sabr1n4W Jul 8, 2026
6302074
Improve saved access parameter priority
Sabr1n4W Jul 8, 2026
5b8acaf
Cleanup
Sabr1n4W Jul 8, 2026
b91f184
Improve padding inside room tab section
Sabr1n4W Jul 8, 2026
e6fad45
Cleanup
Sabr1n4W Jul 8, 2026
9175f22
Improve personalized links hash and sessionStorage handling
Sabr1n4W Jul 11, 2026
330f562
Update resources/js/components/RoomJoinButton.vue
Sabr1n4W Jul 11, 2026
cca1e54
Update resources/js/components/RoomAccessOverlay.vue
Sabr1n4W Jul 11, 2026
7300ae4
Combine load and reload, add listener for hash changes and improve er…
Sabr1n4W Jul 20, 2026
d03fe1f
Improve error handling and loading states
Sabr1n4W Jul 20, 2026
f149d81
Change to full page reload on hash change
Sabr1n4W Jul 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Accessibility: Landmark role to all search fields ([#3264])
- Accessibility: Skip links ([#3264])
- Accessibility: Announce page change to screen readers ([#3264])
- Guests can now choose to remember their name for future video conferences ([#2450], [#3275])
- Room access is now preserved across page reloads after entering via an access code or personalized link ([#3275])
- Login button inside room access overlay to allow users to log in instead of accessing the room as a guest ([#2450], [#3275])

### Changed

Expand All @@ -24,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Accessibility: Move keyboard focus to page start on page change ([#3264])
- Accessibility: Reduced flipping words animation on loading page for browsers with reduced-motion set ([#3277])
- Room share dialog now copies the room link and invitation message to the clipboard in both plain text and HTML format ([#3296])
- Guest name input was moved from join dialog to room access overlay ([#2450], [#3275])
- Room share link now includes the access code, so users no longer need to enter it manually when opening the link ([#3275])
- Improved fallback behavior for invalid room and user tab links ([#3275])

## [v4.16.0] - 2026-06-12

Expand Down Expand Up @@ -767,6 +773,7 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#2383]: https://github.com/THM-Health/PILOS/issues/2383
[#2433]: https://github.com/THM-Health/PILOS/pull/2433
[#2449]: https://github.com/THM-Health/PILOS/pull/2449
[#2450]: https://github.com/THM-Health/PILOS/issues/2450
[#2476]: https://github.com/THM-Health/PILOS/issues/2476
[#2477]: https://github.com/THM-Health/PILOS/pull/2477
[#2478]: https://github.com/THM-Health/PILOS/issues/2478
Expand Down Expand Up @@ -855,6 +862,7 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#3198]: https://github.com/THM-Health/PILOS/pull/3198
[#3215]: https://github.com/THM-Health/PILOS/pull/3215
[#3264]: https://github.com/THM-Health/PILOS/pull/3264
[#3275]: https://github.com/THM-Health/PILOS/pull/3275
[#3277]: https://github.com/THM-Health/PILOS/pull/3277
[#3296]: https://github.com/THM-Health/PILOS/pull/3296
[unreleased]: https://github.com/THM-Health/PILOS/compare/v4.16.0...develop
Expand Down
3 changes: 1 addition & 2 deletions app/Http/Requests/JoinMeetingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace App\Http\Requests;

use App\Rules\ValidName;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Context;

Expand All @@ -15,7 +14,7 @@ public function rules(): array
$personalizedLink = Context::getHidden("room.{$this->room->id}.personalized_link");

$rules = [
'name' => auth()->check() || $personalizedLink ? [] : ['bail', 'required', 'min:2', 'max:50', new ValidName],
'name' => auth()->check() || $personalizedLink ? [] : ValidateParticipantNameRequest::participantNameValidationRules(),
'dark_mode' => ['sometimes', 'boolean'],
];

Expand Down
3 changes: 1 addition & 2 deletions app/Http/Requests/StartMeetingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace App\Http\Requests;

use App\Rules\ValidName;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Context;

Expand All @@ -15,7 +14,7 @@ public function rules(): array
$personalizedLink = Context::getHidden("room.{$this->room->id}.personalized_link");

$rules = [
'name' => auth()->check() || $personalizedLink ? [] : ['bail', 'required', 'min:2', 'max:50', new ValidName],
'name' => auth()->check() || $personalizedLink ? [] : ValidateParticipantNameRequest::participantNameValidationRules(),
'dark_mode' => ['sometimes', 'boolean'],
];

Expand Down
23 changes: 23 additions & 0 deletions app/Http/Requests/ValidateParticipantNameRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace App\Http\Requests;

use App\Rules\ValidName;
use Illuminate\Foundation\Http\FormRequest;

class ValidateParticipantNameRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => self::participantNameValidationRules(),
];
}

public static function participantNameValidationRules(): array
{
return ['bail', 'required', 'min:2', 'max:50', new ValidName];
}
}
1 change: 1 addition & 0 deletions lang/en/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
'next_page' => 'Next page',
'no' => 'No',
'not_found' => '404 | The requested address was not found',
'or' => 'or',
'overwrite' => 'Overwrite',
'previous_page' => 'Previous page',
'profile' => 'Profile',
Expand Down
1 change: 1 addition & 0 deletions lang/en/auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
'logout_success' => 'Successfully logged out',
'new_password' => 'New password',
'new_password_confirmation' => 'New password confirmation',
'offer_login' => 'Login with your user account',
'oidc' => [
'logout_incomplete' => 'You are still logged in at the OpenID Connect provider.',
'redirect' => 'Log in',
Expand Down
5 changes: 5 additions & 0 deletions lang/en/rooms.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
'meetingForciblyEnded' => 'Failed to join meeting: The meeting is no longer running.',
],
'become_member' => 'Become member',
'change_participant_name' => 'Change name',
'change_type' => [
'changing_settings' => 'The following changes will be made to the room settings',
'current_setting' => 'Current setting',
Expand All @@ -24,6 +25,7 @@
'detached' => 'Connection to ongoing video conference lost, recovery failed.',
'reconnecting' => 'Connection to ongoing video conference lost, trying to reconnect.',
],
'continue_as_guest' => 'Continue as guest',
'create' => [
'ok' => 'Create',
'title' => 'Create room',
Expand Down Expand Up @@ -273,6 +275,7 @@
],
],
'name' => 'Room name',
'name_in_video_conference' => 'Name in video conference: ',
'no_rooms_available' => 'No rooms available',
'no_rooms_found' => 'No rooms found',
'not_running' => 'This room is not started yet.',
Expand Down Expand Up @@ -367,6 +370,8 @@
'title' => 'Recordings',
'view_recording' => 'View recording',
],
'remember_participant_name' => 'Remember name for next time',
'request_participant_name_change' => 'Please change your name to join the room.',
'require_access_code' => 'An access code is required to join this room',
'role' => 'Role',
'roles' => [
Expand Down
237 changes: 237 additions & 0 deletions resources/js/components/RoomAccessOverlay.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
<template>
<Card
style="width: 500px; max-width: 90vw"
:pt="{ header: { class: 'flex justify-center' } }"
data-test="room-access-overlay"
>
<template #header>
<Badge
Comment thread
Sabr1n4W marked this conversation as resolved.
v-if="!room.authenticated"
severity="danger"
class="-mt-8 flex !h-16 !w-16 items-center justify-center rounded-full"
>
<i class="fa-solid fa-lock text-2xl text-white"></i>
</Badge>
Comment thread
samuelwei marked this conversation as resolved.
</template>
<template #content>
<RoomHeader
:room="room"
:loading="loading || isLoadingAction"
:details-inline="false"
:hide-favorites="true"
:hide-membership="true"
:disable-reload="authThrottledFor > 0"
:bbb-errors="bbbErrors"
:bbb-reason="bbbReason"
@reload="emit('reload')"
/>

<div
v-if="!authStore.isAuthenticated"
class="mt-4 flex w-full flex-col gap-2"
>
<Button
data-test="room-login-as-user-button"
icon="fa-solid fa-right-to-bracket"
:label="$t('auth.offer_login')"
as="router-link"
:to="{ name: 'login', query: { redirect: $route.path } }"
/>

<Divider class="m-1">{{ $t("app.or").toUpperCase() }}</Divider>
</div>

<Form :disabled="authThrottledFor > 0" @submit="submit">
<div
v-if="!authStore.isAuthenticated"
class="field flex flex-col gap-2"
data-test="participant-name-field"
>
<label for="participant-name">{{
$t("rooms.first_and_lastname")
}}</label>
<InputText
id="participant-name"
v-model="participantNameInput"
:disabled="authThrottledFor > 0 || loading || isLoadingAction"
:invalid="formErrors.fieldInvalid('name')"
/>

<div class="flex items-center gap-2">
<Checkbox
v-model="rememberParticipantName"
input-id="remember-participant-name"
:disabled="authThrottledFor > 0 || loading || isLoadingAction"
binary
/>
<label for="remember-participant-name">
{{ $t("rooms.remember_participant_name") }}
</label>
</div>

<FormError :errors="formErrors.fieldError('name')" />
</div>

<div
v-if="!room.authenticated"
class="field mt-4 flex flex-col gap-2"
data-test="access-code-field"
>
<label for="access-code">{{ $t("rooms.access_code") }}</label>
<InputMask
id="access-code"
v-model="accessCodeInput"
autofocus
:mask="room.legacy_code ? '******' : '999-999-999'"
:placeholder="room.legacy_code ? '123abc' : '123-456-789'"
:invalid="
accessCodeInvalid || formErrors.fieldInvalid('access_code')
"
:disabled="authThrottledFor > 0 || loading || isLoadingAction"
class="text-center"
@keydown.enter="submit"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
/>
Comment thread
Sabr1n4W marked this conversation as resolved.
<FormError :errors="formErrors.fieldError('access_code')" />
<FormError :errors="accessCodeErrors" />
</div>

<Button
class="mt-6 w-full"
type="submit"
:loading="loading || isLoadingAction"
:label="
authStore.isAuthenticated
? $t('app.continue')
: $t('rooms.continue_as_guest')
"
data-test="room-login-button"
:disabled="authThrottledFor > 0 || loading || isLoadingAction"
/>
</Form>
</template>
</Card>
</template>

<script setup>
import RoomHeader from "./RoomHeader.vue";
import { useAuthStore } from "../stores/auth.js";
import { computed, onMounted, ref, watch } from "vue";
import { useApi } from "../composables/useApi.js";
import { HTTP_STATUS_UNPROCESSABLE_ENTITY } from "../constants/httpStatusCodes.js";
import { useI18n } from "vue-i18n";

const emit = defineEmits(["submit", "reload"]);
const props = defineProps({
room: {
type: Object,
required: true,
},
loading: {
type: Boolean,
default: false,
},
authThrottledFor: {
type: Number,
default: 0,
},
accessCodeInvalid: {
type: Boolean,
default: false,
},
formErrors: {
type: Object,
required: true,
},
bbbErrors: {
type: String,
default: null,
},
bbbReason: {
type: String,
default: null,
},
});

const accessCode = defineModel("accessCode", {
type: String,
default: "",
});

const participantName = defineModel("participantName", {
type: String,
default: "",
});

const rememberParticipantName = defineModel("rememberParticipantName", {
type: Boolean,
default: false,
});
const participantNameInput = ref("");
const accessCodeInput = ref("");
const isLoadingAction = ref(false);

const authStore = useAuthStore();
const api = useApi();
const { t } = useI18n();

const accessCodeErrors = computed(() => {
if (props.authThrottledFor > 0) {
return [t("rooms.auth_throttled", { try_again: props.authThrottledFor })];
}

if (props.accessCodeInvalid) {
return [t("rooms.flash.access_code_invalid")];
}

return [];
});

onMounted(() => {
participantNameInput.value = participantName.value;
accessCodeInput.value = accessCode.value;
});

watch(participantName, (value) => {
participantNameInput.value = value;
});
Comment on lines +194 to +196

function submit() {
if (authStore.isAuthenticated) {
accessCode.value = accessCodeInput.value;
emit("submit");
return;
}

props.formErrors.clear();
isLoadingAction.value = true;

const newParticipantName = participantNameInput.value;

api
.call("participantName/check", {
method: "post",
data: {
name: newParticipantName,
},
})
.then(() => {
accessCode.value = accessCodeInput.value;
participantName.value = newParticipantName;
emit("submit");
})
.catch((error) => {
if (
error.response &&
error.response.status === HTTP_STATUS_UNPROCESSABLE_ENTITY
) {
props.formErrors.set(error.response.data.errors);
return;
}

api.error(error);
})
.finally(() => {
isLoadingAction.value = false;
});
}
</script>
4 changes: 2 additions & 2 deletions resources/js/components/RoomBBBMessage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ watch(errorMessages, (errors) => {
<Message
v-if="reason"
data-test="room-meeting-ended-reason"
class="mb-3"
class="mb-2"
closable
@close="closeReasonMessage"
>{{ reason }}</Message
>

<!-- Show error messages -->
<div v-if="errorMessages" class="mb-3 flex flex-col gap-3">
<div v-if="errorMessages" class="mb-2 flex flex-col gap-3">
<Message
v-for="error in errorMessages"
:key="error.key"
Expand Down
Loading
Loading