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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Environment variable `VERSION` to change the displayed version in the footer ([#3300], [#3302])
- System-wide default welcome message ([#3301])
- Privacy setting to disable finding users by partial matches of their name or email address ([#2264], [#3316])
Comment thread
samuelwei marked this conversation as resolved.

### Fixed

Expand Down Expand Up @@ -764,6 +765,7 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#2165]: https://github.com/THM-Health/PILOS/pull/2165
[#2222]: https://github.com/THM-Health/PILOS/pull/2222
[#2223]: https://github.com/THM-Health/PILOS/pull/2223
[#2264]: https://github.com/THM-Health/PILOS/issues/2264
[#2265]: https://github.com/THM-Health/PILOS/issues/2265
[#2279]: https://github.com/THM-Health/PILOS/pull/2279
[#2281]: https://github.com/THM-Health/PILOS/pull/2281
Expand Down Expand Up @@ -873,6 +875,7 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#3302]: https://github.com/THM-Health/PILOS/pull/3302
[#3314]: https://github.com/THM-Health/PILOS/issues/3314
[#3315]: https://github.com/THM-Health/PILOS/pull/3315
[#3316]: https://github.com/THM-Health/PILOS/pull/3316
[unreleased]: https://github.com/THM-Health/PILOS/compare/v4.17.0...develop
[v3.0.0]: https://github.com/THM-Health/PILOS/releases/tag/v3.0.0
[v3.0.1]: https://github.com/THM-Health/PILOS/releases/tag/v3.0.1
Expand Down
1 change: 1 addition & 0 deletions app/Http/Controllers/api/v1/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ public function update(UpdateSettingsRequest $request)
$roomSettings->hide_owner_from_guests = $request->boolean('room_hide_owner_from_guests');

$userSettings->password_change_allowed = $request->boolean('user_password_change_allowed');
$userSettings->search_by_name = $request->boolean('user_search_by_name');

$bannerSettings->enabled = $request->boolean('banner_enabled');
$bannerSettings->title = $request->input('banner_title');
Expand Down
16 changes: 14 additions & 2 deletions app/Http/Controllers/api/v1/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use App\Services\AuthenticationService;
use App\Services\EmailVerification\EmailVerificationService;
use App\Settings\GeneralSettings;
use App\Settings\UserSettings;
use Carbon\Carbon;
use Exception;
use Illuminate\Auth\Access\AuthorizationException;
Expand All @@ -31,6 +32,7 @@
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class UserController extends Controller
{
Expand All @@ -41,18 +43,28 @@ public function __construct()
}

/**
* Search for users in the whole database, based on first name and last name
* Search for users in the whole database
* Depending on the search_by_name setting it will either search for partial matches of firstname, lastname and email
* or only for an exact email match
*
* @param UserSearchRequest $request query parameter with search query
* @return AnonymousResourceCollection
*/
public function search(UserSearchRequest $request)
{
$userSettings = app(UserSettings::class);

if (! $request->filled('query')) {
abort(204, 'Too many results');
}

$query = User::withNameOrEmail($request->query('query'));
if ($userSettings->search_by_name) {
$query = User::withNameOrEmail($request->query('query'));
} else {
// Escape wildcard characters and escape backslashes to prevent escaping our added escape backslash
$email = Str::replace(['\\', '%', '_'], ['\\\\', '\%', '\_'], $request->query('query'));
$query = User::whereLike('email', $email);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if ($query->count() > config('bigbluebutton.user_search_limit')) {
abort(204, 'Too many results');
Expand Down
1 change: 1 addition & 0 deletions app/Http/Requests/UpdateSettingsRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public function rules()
'room_hide_owner_from_guests' => ['required', 'boolean'],

'user_password_change_allowed' => ['required', 'boolean'],
'user_search_by_name' => ['required', 'boolean'],

'recording_server_usage_enabled' => ['required', 'boolean'],
'recording_server_usage_retention_period' => ['required', 'numeric', Rule::enum(TimePeriod::class)],
Expand Down
1 change: 1 addition & 0 deletions app/Http/Resources/ConfigResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public function toArray($request)
],
'user' => [
'password_change_allowed' => $userSettings->password_change_allowed,
'search_by_name' => $userSettings->search_by_name,
],
'bbb' => [
'file_mimes' => config('bigbluebutton.allowed_file_mimes'),
Expand Down
1 change: 1 addition & 0 deletions app/Http/Resources/SettingsResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public function toArray($request)
'room_file_terms_of_use' => $roomSettings->file_terms_of_use,
'room_hide_owner_from_guests' => $roomSettings->hide_owner_from_guests,
'user_password_change_allowed' => $userSettings->password_change_allowed,
'user_search_by_name' => $userSettings->search_by_name,
'recording_server_usage_enabled' => $recordingSettings->server_usage_enabled,
'recording_server_usage_retention_period' => $recordingSettings->server_usage_retention_period,
'recording_meeting_usage_enabled' => $recordingSettings->meeting_usage_enabled,
Expand Down
2 changes: 2 additions & 0 deletions app/Settings/UserSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class UserSettings extends Settings
{
public bool $password_change_allowed;

public bool $search_by_name;

public static function group(): string
{
return 'user';
Expand Down
18 changes: 18 additions & 0 deletions database/settings/2026_07_15_130614_add_user_search_by_name.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

use Spatie\LaravelSettings\Migrations\SettingsMigration;

return new class extends SettingsMigration
{
public function up(): void
{
$this->migrator->add('user.search_by_name', true);
}

public function down(): void
{
$this->migrator->delete('user.search_by_name');
}
};
4 changes: 4 additions & 0 deletions lang/en/admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,10 @@
],
'two_weeks' => '2 Weeks (14 Days)',
'two_years' => '2 Years (730 Days)',
'user_search_by_name' => [
'description' => 'Allows users to find others by partial matches of their name or email address. When disabled, users can only be found by entering their exact email address.',
'title' => 'Allow searching users by name',
],
'user_settings' => 'User settings',
],
'streaming' => [
Expand Down
3 changes: 3 additions & 0 deletions lang/en/rooms.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,11 @@
'add' => [
'add' => 'Add',
'no_options' => 'Enter the name or email address of a user.',
'no_options_email_only' => 'Enter the email address of a user.',
'no_result' => 'Oops! No user was found for this query.',
'placeholder' => 'Name or email',
'placeholder_email_only' => 'Email',
'searching' => 'Searching...',
'select_role' => 'Please select a role',
'select_user' => 'Please select the user you would like to add',
'too_many_results' => 'Too many users were found. Please enter a more precise search term.',
Expand Down
87 changes: 4 additions & 83 deletions resources/js/components/RoomTabMembersAddSingleModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,51 +39,13 @@
<!-- select user -->
<div class="field relative mt-2 flex flex-col gap-2 overflow-visible">
<label id="user-label">{{ $t("app.user") }}</label>
<multiselect
<UserSearch
v-model="user"
:disabled="isLoadingAction"
:invalid="formErrors.fieldInvalid('user')"
aria-labelledby="user-label"
autofocus
data-test="select-user-dropdown"
label="lastname"
track-by="id"
:placeholder="$t('rooms.members.modals.add.placeholder')"
open-direction="bottom"
:options="users"
:multiple="false"
:searchable="true"
:loading="isLoadingSearch"
:disabled="isLoadingAction"
:internal-search="false"
:clear-on-select="false"
:preserve-search="true"
:close-on-select="true"
:options-limit="300"
:max-height="150"
:show-no-results="true"
:show-labels="false"
:class="{ 'is-invalid': formErrors.fieldInvalid('user') }"
@search-change="asyncFind"
>
<template #noResult>
<span v-if="tooManyResults" class="whitespace-normal">
{{ $t("rooms.members.modals.add.too_many_results") }}
</span>
<span v-else>
{{ $t("rooms.members.modals.add.no_result") }}
</span>
</template>
<template #noOptions>
{{ $t("rooms.members.modals.add.no_options") }}
</template>
<template #option="{ option }">
{{ option.firstname }} {{ option.lastname }}<br /><small>{{
option.email
}}</small>
</template>
<template #singleLabel="{ option }">
{{ option.firstname }} {{ option.lastname }}
</template>
</multiselect>
/>
<FormError :errors="formErrors.fieldError('user')" />
</div>

Expand Down Expand Up @@ -144,7 +106,6 @@
</Dialog>
</template>
<script setup>
import Multiselect from "vue-multiselect";
import { useApi } from "../composables/useApi.js";
import { useFormErrors } from "../composables/useFormErrors.js";
import { ref } from "vue";
Expand All @@ -162,64 +123,24 @@ const props = defineProps({
});

const emit = defineEmits(["added"]);

const api = useApi();
const formErrors = useFormErrors();

const modalVisible = ref(false);
const user = ref(null);
const role = ref(null);
const users = ref([]);
const tooManyResults = ref(false);
const isLoadingSearch = ref(false);
const isLoadingAction = ref(false);

defineExpose({
showModal,
});

/**
* Search for users in database
* @param query
*/
function asyncFind(query) {
isLoadingSearch.value = true;

const config = {
params: {
query,
},
};

api
.call("users/search", config)
.then((response) => {
if (response.status === 204) {
users.value = [];
tooManyResults.value = true;
return;
}

users.value = response.data.data;
tooManyResults.value = false;
})
.catch((error) => {
tooManyResults.value = false;
api.error(error, { redirectOnUnauthenticated: false });
})
.finally(() => {
isLoadingSearch.value = false;
});
}

/**
* show modal to add a new user as member
*/
function showModal() {
user.value = null;
role.value = null;
formErrors.clear();
users.value = [];
modalVisible.value = true;
}

Expand Down
Loading
Loading