Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Improved error handling when a room requires an access code but none was provided ([#3035])
- Improved 404 error messages for better readability and clarity ([#86], [#3036])
- Standardized 404 error handling on admin pages ([#1676], [#3036])
- Improved and standardized room not found error handling in the room view ([#3036])

## [v4.14.2] - 2026-04-10

Expand Down Expand Up @@ -544,6 +547,7 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#31]: https://github.com/THM-Health/PILOS/issues/31
[#75]: https://github.com/THM-Health/PILOS/issues/75
[#77]: https://github.com/THM-Health/PILOS/issues/77
[#86]: https://github.com/THM-Health/PILOS/issues/86
[#300]: https://github.com/THM-Health/PILOS/issues/300
[#315]: https://github.com/THM-Health/PILOS/issues/315
[#372]: https://github.com/THM-Health/PILOS/issues/372
Expand Down Expand Up @@ -640,6 +644,7 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#1636]: https://github.com/THM-Health/PILOS/issues/1636
[#1651]: https://github.com/THM-Health/PILOS/issues/1651
[#1675]: https://github.com/THM-Health/PILOS/issues/1675
[#1676]: https://github.com/THM-Health/PILOS/issues/1676
[#1677]: https://github.com/THM-Health/PILOS/issues/1677
[#1678]: https://github.com/THM-Health/PILOS/pull/1678
[#1679]: https://github.com/THM-Health/PILOS/issues/1679
Expand Down Expand Up @@ -774,6 +779,7 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#3028]: https://github.com/THM-Health/PILOS/issues/3028
[#3029]: https://github.com/THM-Health/PILOS/pull/3029
[#3035]: https://github.com/THM-Health/PILOS/pull/3035
[#3036]: https://github.com/THM-Health/PILOS/pull/3036
[#3039]: https://github.com/THM-Health/PILOS/issues/3039
[#3040]: https://github.com/THM-Health/PILOS/pull/3040
[unreleased]: https://github.com/THM-Health/PILOS/compare/v4.14.2...develop
Expand Down
19 changes: 19 additions & 0 deletions app/Exceptions/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace App\Exceptions;

use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Foundation\ViteException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Psr\Log\LogLevel;
use Spatie\LaravelIgnition\Exceptions\ViewException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;

class Handler extends ExceptionHandler
Expand Down Expand Up @@ -77,5 +80,21 @@ public function register(): void

return null;
});

$this->renderable(function (NotFoundHttpException $e, Request $request) {
if ($request->expectsJson()) {
$modelNotFoundException = $e->getPrevious() instanceof ModelNotFoundException ? $e->getPrevious() : null;

if ($modelNotFoundException) {
$json = [
'message' => 'model_not_found',
'model' => Str::snake(class_basename($modelNotFoundException->getModel())),
'ids' => $modelNotFoundException->getIds(),
];

return response()->json($json, 404);
}
}
});
}
}
18 changes: 6 additions & 12 deletions app/Http/Controllers/api/v1/RoomMemberController.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,11 @@ public function bulkImport(Room $room, BulkImportRequest $request)
*
* @return Response
*/
public function update(Room $room, User $user, UpdateRoomMemberRequest $request)
public function update(Room $room, User $member, UpdateRoomMemberRequest $request)
{
if (! $room->members->contains($user)) {
abort(410, __('app.errors.not_member_of_room'));
}
$room->members()->updateExistingPivot($user, ['role' => $request->role]);
$room->members()->updateExistingPivot($member, ['role' => $request->role]);

Log::info('Changed role for member {member} to {role} in room {room}', ['room' => $room->getLogLabel(), 'role' => RoomUserRole::from($request->role)->label(), 'member' => $user->getLogLabel()]);
Log::info('Changed role for member {member} to {role} in room {room}', ['room' => $room->getLogLabel(), 'role' => RoomUserRole::from($request->role)->label(), 'member' => $member->getLogLabel()]);

return response()->noContent();
}
Expand All @@ -155,14 +152,11 @@ public function bulkUpdate(Room $room, BulkUpdateRequest $request)
*
* @return Response
*/
public function destroy(Room $room, User $user)
public function destroy(Room $room, User $member)
{
if (! $room->members->contains($user)) {
abort(410, __('app.errors.not_member_of_room'));
}
$room->members()->detach($user);
$room->members()->detach($member);

Log::info('Removed member {member} from room {room}', ['room' => $room->getLogLabel(), 'member' => $user->getLogLabel()]);
Log::info('Removed member {member} from room {room}', ['room' => $room->getLogLabel(), 'member' => $member->getLogLabel()]);

return response()->noContent();
}
Expand Down
28 changes: 10 additions & 18 deletions app/Http/Controllers/api/v1/RoomPersonalizedLinkController.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,20 +103,16 @@ public function store(Room $room, RoomPersonalizedLinkRequest $request)
*
* @return RoomPersonalizedLinkResource
*/
public function update(Room $room, RoomPersonalizedLink $link, RoomPersonalizedLinkRequest $request)
public function update(Room $room, RoomPersonalizedLink $personalizedLink, RoomPersonalizedLinkRequest $request)
{
if (! $link->room->is($room)) {
abort(404, __('app.errors.personalized_link_not_found'));
}

$link->firstname = $request->firstname;
$link->lastname = $request->lastname;
$link->role = $request->role;
$link->save();
$personalizedLink->firstname = $request->firstname;
$personalizedLink->lastname = $request->lastname;
$personalizedLink->role = $request->role;
$personalizedLink->save();

Log::info('Updated personalized room link for guest {name} with the role {role} for room {room}', ['room' => $room->getLogLabel(), 'role' => $link->role->label(), 'name' => $link->fullname]);
Log::info('Updated personalized room link for guest {name} with the role {role} for room {room}', ['room' => $room->getLogLabel(), 'role' => $personalizedLink->role->label(), 'name' => $personalizedLink->fullname]);

return new RoomPersonalizedLinkResource($link);
return new RoomPersonalizedLinkResource($personalizedLink);
}

/**
Expand All @@ -126,15 +122,11 @@ public function update(Room $room, RoomPersonalizedLink $link, RoomPersonalizedL
*
* @throws \Exception
*/
public function destroy(Room $room, RoomPersonalizedLink $link)
public function destroy(Room $room, RoomPersonalizedLink $personalizedLink)
{
if (! $link->room->is($room)) {
abort(404, __('app.errors.personalized_link_not_found'));
}

$link->delete();
$personalizedLink->delete();

Log::info('Removed personalized room link for guest {name} with the role {role} for room {room}', ['room' => $room->getLogLabel(), 'role' => $link->role->label(), 'name' => $link->fullname]);
Log::info('Removed personalized room link for guest {name} with the role {role} for room {room}', ['room' => $room->getLogLabel(), 'role' => $personalizedLink->role->label(), 'name' => $personalizedLink->fullname]);

return response()->noContent();
}
Expand Down
17 changes: 16 additions & 1 deletion lang/en/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
'membership_disabled' => 'Membership failed! Membership for this room is currently not available.',
'no_room_access' => 'You does not have the necessary permissions, to edit this room.',
'no_server_available' => 'Currently there are no servers available.',
'not_member_of_room' => 'The person is not a member of this room (anymore).',
'not_member_of_room' => 'The user is not a member of this room.',
'not_running' => 'Joining the room has failed as it is currently closed.',
'personalized_link_not_found' => 'The personalized room link could not be found.',
'record_agreement_missing' => 'Consent to the recording is required.',
Expand All @@ -66,6 +66,10 @@
'flash' => [
'client_error' => 'An unknown error occurred in the application!',
'guests_only' => 'The request can only be made by guests!',
'model_not_found' => [
'title' => 'The :model was not found!',
'details' => 'Ids: :ids',
],
Comment thread
coderabbitai[bot] marked this conversation as resolved.
'server_error' => [
'empty_message' => 'An error occurred on the server during request!',
'error_code' => 'Error code: :statusCode',
Expand Down Expand Up @@ -97,10 +101,21 @@
'fr' => 'French',
],
'model' => [
'meeting' => 'Meeting',
'recording' => 'Recording',
'recording_format' => 'Recording format',
'role' => 'Role',
'roles' => 'role',
'room' => 'Room',
'room_file' => 'Room file',
'room_personalized_link' => 'Room personalized link',
'room_type' => 'Room type',
'room_types' => 'room type',
'server' => 'Server',
'server_pool' => 'Server pool',
'server_pools' => 'server pool',
'servers' => 'server',
'user' => 'User',
'users' => 'user',
],
'model_name' => 'Name',
Expand Down
2 changes: 2 additions & 0 deletions resources/js/components/RoomCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<RoomFavoriteButton
:room="props.room"
class="room-card-button h-8 w-8 p-0 text-sm"
:redirect-on-room-model-not-found="false"
@favorites-changed="$emit('favoritesChanged')"
/>
</div>
Expand Down Expand Up @@ -72,6 +73,7 @@
<div class="room-card-buttons shrink-0">
<RoomFavoriteButton
:room="props.room"
:redirect-on-room-model-not-found="false"
@favorites-changed="$emit('favoritesChanged')"
/>
</div>
Expand Down
5 changes: 5 additions & 0 deletions resources/js/components/RoomFavoriteButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const props = defineProps({
type: Boolean,
default: true,
},
redirectOnRoomModelNotFound: {
type: Boolean,
default: true,
},
});

const isLoading = ref(false);
Expand All @@ -57,6 +61,7 @@ function toggleFavorite() {
.catch((error) => {
api.error(error, {
redirectOnUnauthenticated: props.redirectOnUnauthenticated,
redirectOnRoomModelNotFound: props.redirectOnRoomModelNotFound,
});
})
.finally(() => {
Expand Down
6 changes: 5 additions & 1 deletion resources/js/components/RoomTabFilesDeleteButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { useApi } from "../composables/useApi.js";
import { ref } from "vue";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";
import { ROOM_FILE } from "../constants/modelNames.js";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -101,7 +102,10 @@ function deleteFile() {
// deleting failed
if (error.response) {
// file not found
if (error.response.status === env.HTTP_NOT_FOUND) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === ROOM_FILE
) {
toast.error(t("rooms.flash.file_gone"));
emit("notFound");
modalVisible.value = false;
Expand Down
6 changes: 5 additions & 1 deletion resources/js/components/RoomTabFilesEditButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ import { ref, watch } from "vue";
import { useFormErrors } from "../composables/useFormErrors.js";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";
import { ROOM_FILE } from "../constants/modelNames.js";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -195,7 +196,10 @@ function save() {
// editing failed
if (error.response) {
// file not found
if (error.response.status === env.HTTP_NOT_FOUND) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === ROOM_FILE
) {
toast.error(t("rooms.flash.file_gone"));
emit("notFound");
modalVisible.value = false;
Expand Down
12 changes: 11 additions & 1 deletion resources/js/components/RoomTabMembersDeleteButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
import env from "../env";
import { useApi } from "../composables/useApi.js";
import { ref } from "vue";
import { USER } from "../constants/modelNames.js";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -83,6 +86,8 @@ const props = defineProps({
const emit = defineEmits(["deleted", "gone"]);

const api = useApi();
const toast = useToast();
const { t } = useI18n();

const modalVisible = ref(false);
const isLoadingAction = ref(false);
Expand All @@ -106,9 +111,14 @@ function deleteMember() {
// editing failed
if (error.response) {
// user not found
if (error.response.status === env.HTTP_GONE) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === USER
) {
toast.error(t("app.errors.not_member_of_room"));
emit("gone");
modalVisible.value = false;
return;
}
}
api.error(error, { redirectOnUnauthenticated: false });
Expand Down
12 changes: 11 additions & 1 deletion resources/js/components/RoomTabMembersEditButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ import env from "../env";
import { useApi } from "../composables/useApi.js";
import { useFormErrors } from "../composables/useFormErrors.js";
import { ref } from "vue";
import { USER } from "../constants/modelNames.js";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -131,6 +134,8 @@ const emit = defineEmits(["edited", "gone"]);

const api = useApi();
const formErrors = useFormErrors();
const toast = useToast();
const { t } = useI18n();

const modalVisible = ref(false);
const newRole = ref(null);
Expand Down Expand Up @@ -168,9 +173,14 @@ function save() {
// editing failed
if (error.response) {
// user not found
if (error.response.status === env.HTTP_GONE) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === USER
) {
toast.error(t("app.errors.not_member_of_room"));
emit("gone");
modalVisible.value = false;
return;
}
// failed due to form validation errors
if (error.response.status === env.HTTP_UNPROCESSABLE_ENTITY) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { useApi } from "../composables/useApi.js";
import { ref } from "vue";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";
import { ROOM_PERSONALIZED_LINK } from "../constants/modelNames.js";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -120,7 +121,10 @@ function deleteLink() {
// deleting failed
if (error.response) {
// personalized link not found
if (error.response.status === env.HTTP_NOT_FOUND) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === ROOM_PERSONALIZED_LINK
) {
toast.error(t("rooms.flash.personalized_link_gone"));
modalVisible.value = false;
emit("notFound");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ import { ref } from "vue";
import env from "../env.js";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";
import { ROOM_PERSONALIZED_LINK } from "../constants/modelNames.js";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -189,7 +190,10 @@ function save() {
// editing failed
if (error.response) {
// token not found
if (error.response.status === env.HTTP_NOT_FOUND) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === ROOM_PERSONALIZED_LINK
) {
toast.error(t("rooms.flash.personalized_link_gone"));
modalVisible.value = false;
emit("notFound");
Expand Down
Loading
Loading