diff --git a/app/Http/Requests/StoreRoomFileRequest.php b/app/Http/Requests/StoreRoomFileRequest.php index 12242b692..c6153b3b3 100644 --- a/app/Http/Requests/StoreRoomFileRequest.php +++ b/app/Http/Requests/StoreRoomFileRequest.php @@ -6,13 +6,14 @@ use App\Rules\Antivirus; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rules\File; class StoreRoomFileRequest extends FormRequest { public function rules() { return [ - 'file' => ['bail', 'required', 'file', 'max:'.(config('bigbluebutton.max_filesize') * 1000), 'mimes:'.config('bigbluebutton.allowed_file_mimes'), new Antivirus], // https://github.com/bigbluebutton/bigbluebutton/blob/v2.2.x-release/bigbluebutton-html5/private/config/settings.yml + 'file' => ['bail', 'required', File::types(config('bigbluebutton.allowed_file_mimes'))->extensions(config('bigbluebutton.allowed_file_mimes'))->max(config('bigbluebutton.max_filesize').'mb'), new Antivirus], // https://github.com/bigbluebutton/bigbluebutton/blob/v2.2.x-release/bigbluebutton-html5/private/config/settings.yml ]; } } diff --git a/app/Http/Requests/UpdateRoomStreamingConfigRequest.php b/app/Http/Requests/UpdateRoomStreamingConfigRequest.php index 75d58ab36..ae3564ea0 100644 --- a/app/Http/Requests/UpdateRoomStreamingConfigRequest.php +++ b/app/Http/Requests/UpdateRoomStreamingConfigRequest.php @@ -6,6 +6,8 @@ use App\Rules\Antivirus; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\File; class UpdateRoomStreamingConfigRequest extends FormRequest { @@ -19,7 +21,7 @@ public function rules() return [ 'enabled' => ['required', 'boolean'], 'url' => ['nullable', 'required_if_accepted:enabled', 'string', 'url:rtmp,rtmps', 'max:255'], - 'pause_image' => ['bail', 'nullable', 'image', 'mimes:jpg,bmp,png,gif', 'max:5000', 'dimensions:width=1920,height=1080', new Antivirus], // 5 MB + 'pause_image' => ['bail', 'nullable', File::types(['jpg', 'bmp', 'png', 'gif'])->extensions(['jpg', 'jpeg', 'bmp', 'png', 'gif'])->max('5mb'), Rule::dimensions()->width(1920)->height(1080), new Antivirus], ]; } diff --git a/app/Http/Requests/UpdateRoomTypeStreamingSettingsRequest.php b/app/Http/Requests/UpdateRoomTypeStreamingSettingsRequest.php index 62b3fb5ba..bdbb4cd78 100644 --- a/app/Http/Requests/UpdateRoomTypeStreamingSettingsRequest.php +++ b/app/Http/Requests/UpdateRoomTypeStreamingSettingsRequest.php @@ -6,6 +6,8 @@ use App\Rules\Antivirus; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\File; class UpdateRoomTypeStreamingSettingsRequest extends FormRequest { @@ -18,7 +20,7 @@ public function rules() { return [ 'enabled' => ['required', 'boolean'], - 'default_pause_image' => ['bail', 'nullable', 'image', 'mimes:jpg,bmp,png,gif', 'max:5000', 'dimensions:width=1920,height=1080', new Antivirus], // 5 MB + 'default_pause_image' => ['bail', 'nullable', File::types(['jpg', 'bmp', 'png', 'gif'])->extensions(['jpg', 'jpeg', 'bmp', 'png', 'gif'])->max('5mb'), Rule::dimensions()->width(1920)->height(1080), new Antivirus], ]; } } diff --git a/app/Http/Requests/UpdateSettingsRequest.php b/app/Http/Requests/UpdateSettingsRequest.php index 44b98588d..6ee5f9d6a 100644 --- a/app/Http/Requests/UpdateSettingsRequest.php +++ b/app/Http/Requests/UpdateSettingsRequest.php @@ -10,6 +10,7 @@ use App\Rules\Antivirus; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\File; class UpdateSettingsRequest extends FormRequest { @@ -44,16 +45,16 @@ public function rules() 'general_no_welcome_page' => ['required', 'boolean'], 'theme_logo' => ['required_without:theme_logo_file', 'string', 'max:255'], - 'theme_logo_file' => ['bail', 'required_without:theme_logo', 'image:allow_svg', 'max:500', new Antivirus], // 500 KB, larger files are bad for loading times + 'theme_logo_file' => ['bail', 'required_without:theme_logo', File::types(['jpg', 'png', 'gif', 'svg'])->extensions(['jpg', 'jpeg', 'png', 'gif', 'svg'])->max('500kb'), new Antivirus], // 500 KB, larger files are bad for loading times 'theme_logo_dark' => ['required_without:theme_logo_dark_file', 'string', 'max:255'], - 'theme_logo_dark_file' => ['bail', 'required_without:theme_logo_dark', 'image:allow_svg', 'max:500', new Antivirus], // 500 KB, larger files are bad for loading times + 'theme_logo_dark_file' => ['bail', 'required_without:theme_logo_dark', File::types(['jpg', 'png', 'gif', 'svg'])->extensions(['jpg', 'jpeg', 'png', 'gif', 'svg'])->max('500kb'), new Antivirus], // 500 KB, larger files are bad for loading times 'theme_favicon' => ['required_without:theme_favicon_file', 'string', 'max:255'], - 'theme_favicon_file' => ['bail', 'required_without:theme_favicon', 'mimes:ico', 'max:500', new Antivirus], // 500 KB, larger files are bad for loading times + 'theme_favicon_file' => ['bail', 'required_without:theme_favicon', File::types('ico')->extensions('ico')->max('500kb'), new Antivirus], // 500 KB, larger files are bad for loading times 'theme_favicon_dark' => ['required_without:theme_favicon_dark_file', 'string', 'max:255'], - 'theme_favicon_dark_file' => ['bail', 'required_without:theme_favicon_dark', 'mimes:ico', 'max:500', new Antivirus], // 500 KB, larger files are bad for loading times + 'theme_favicon_dark_file' => ['bail', 'required_without:theme_favicon_dark', File::types('ico')->extensions('ico')->max('500kb'), new Antivirus], // 500 KB, larger files are bad for loading times 'theme_primary_color' => ['required', 'string', 'hex_color'], 'theme_rounded' => ['required', 'boolean'], - 'theme_custom_css' => ['bail', 'nullable', 'file', 'max:500', 'extensions:css', new Antivirus], + 'theme_custom_css' => ['bail', 'nullable', File::types(['css', 'txt'])->extensions('css')->max('500kb'), new Antivirus], 'banner_enabled' => ['required', 'boolean'], 'banner_title' => ['nullable', 'string', 'max:255'], @@ -84,12 +85,12 @@ public function rules() 'recording_recording_retention_period' => ['required', 'numeric', Rule::enum(TimePeriod::class)->except($disabledRecordingRetentionPeriods)], 'bbb_logo' => ['nullable', 'string', 'max:255'], - 'bbb_logo_file' => ['bail', 'image:allow_svg', 'max:500', new Antivirus], + 'bbb_logo_file' => ['bail', File::types(['jpg', 'png', 'gif', 'svg'])->extensions(['jpg', 'jpeg', 'png', 'gif', 'svg'])->max('500kb'), new Antivirus], // 500 KB, larger files are bad for loading times 'bbb_logo_dark' => ['nullable', 'string', 'max:255'], - 'bbb_logo_dark_file' => ['bail', 'image:allow_svg', 'max:500', new Antivirus], + 'bbb_logo_dark_file' => ['bail', File::types(['jpg', 'png', 'gif', 'svg'])->extensions(['jpg', 'jpeg', 'png', 'gif', 'svg'])->max('500kb'), new Antivirus], // 500 KB, larger files are bad for loading times - 'bbb_style' => ['bail', 'nullable', 'file', 'max:500', 'extensions:css', new Antivirus], - 'bbb_default_presentation' => ['bail', 'nullable', 'file', 'max:'.(config('bigbluebutton.max_filesize') * 1000), 'mimes:'.config('bigbluebutton.allowed_file_mimes'), new Antivirus], + 'bbb_style' => ['bail', 'nullable', File::types(['css', 'txt'])->extensions('css')->max('500kb'), new Antivirus], // 500 KB, larger files are bad for loading times + 'bbb_default_presentation' => ['bail', 'nullable', File::types(config('bigbluebutton.allowed_file_mimes'))->extensions(config('bigbluebutton.allowed_file_mimes'))->max(config('bigbluebutton.max_filesize').'mb'), new Antivirus], ]; } } diff --git a/app/Http/Requests/UpdateStreamingSettingsRequest.php b/app/Http/Requests/UpdateStreamingSettingsRequest.php index 4e864d01e..4cca6ee51 100644 --- a/app/Http/Requests/UpdateStreamingSettingsRequest.php +++ b/app/Http/Requests/UpdateStreamingSettingsRequest.php @@ -7,6 +7,8 @@ use App\Rules\Antivirus; use App\Rules\CustomJoinMeetingParameters; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\File; class UpdateStreamingSettingsRequest extends FormRequest { @@ -18,8 +20,8 @@ class UpdateStreamingSettingsRequest extends FormRequest public function rules() { return [ - 'default_pause_image' => ['bail', 'nullable', 'image', 'mimes:jpg,bmp,png,gif', 'max:5000', 'dimensions:width=1920,height=1080', new Antivirus], // 5 MB - 'css_file' => ['bail', 'nullable', 'file', 'max:500', 'extensions:css', new Antivirus], + 'default_pause_image' => ['bail', 'nullable', File::types(['jpg', 'bmp', 'png', 'gif'])->extensions(['jpg', 'jpeg', 'bmp', 'png', 'gif'])->max('5mb'), Rule::dimensions()->width(1920)->height(1080), new Antivirus], + 'css_file' => ['bail', 'nullable', File::types(['css', 'txt'])->extensions('css')->max('500kb'), new Antivirus], 'join_parameters' => ['nullable', 'string', 'max:65000', new CustomJoinMeetingParameters], ]; } diff --git a/app/Http/Requests/UserRequest.php b/app/Http/Requests/UserRequest.php index 6d877464e..a8a379c07 100644 --- a/app/Http/Requests/UserRequest.php +++ b/app/Http/Requests/UserRequest.php @@ -9,6 +9,7 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Auth; use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\File; class UserRequest extends FormRequest { @@ -27,7 +28,7 @@ public function rules() 'timezone' => ['sometimes', 'required', Rule::in(timezone_identifiers_list())], 'roles' => ['sometimes', 'required', 'array'], 'roles.*' => ['sometimes', 'distinct', 'integer', 'exists:App\Models\Role,id', Rule::notIn($prohibitedRoles)], - 'image' => ['bail', 'sometimes', 'nullable', 'mimes:jpg', 'dimensions:width=100,height=100', Rule::prohibitedIf($this->user?->has_external_image), new Antivirus], + 'image' => ['bail', 'sometimes', 'nullable', File::types('jpg')->extensions('jpg')->max('20kb'), Rule::dimensions()->width(100)->height(100), Rule::prohibitedIf($this->user?->has_external_image), new Antivirus], ]; if (! $this->user || $this->user->authenticator === 'local') { diff --git a/config/bigbluebutton.php b/config/bigbluebutton.php index e51b67dd8..6805da52d 100644 --- a/config/bigbluebutton.php +++ b/config/bigbluebutton.php @@ -10,7 +10,7 @@ 'secret' => env('BBB_TEST_SERVER_SECRET'), ], 'max_filesize' => (int) env('BBB_MAX_FILESIZE', 30), - 'allowed_file_mimes' => env('BBB_ALLOWED_FILE_MIMES', 'pdf,doc,docx,xls,xlsx,ppt,pptx,txt,rtf,odt,ods,odp,odg,odc,odi,jpg,jpeg,png'), + 'allowed_file_mimes' => explode(',', env('BBB_ALLOWED_FILE_MIMES', 'pdf,doc,docx,xls,xlsx,ppt,pptx,txt,rtf,odt,ods,odp,odg,odc,odi,jpg,jpeg,png')), 'welcome_message_limit' => (int) env('WELCOME_MESSAGE_LIMIT', 500), // max 5000 'room_name_limit' => (int) env('ROOM_NAME_LIMIT', 50), 'room_id_max_tries' => (int) env('BBB_ROOM_ID_MAX_TRIES', 1000), diff --git a/lang/en/admin.php b/lang/en/admin.php index 576d2ea89..d83810cfb 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -473,6 +473,13 @@ 'title' => 'Profile picture', 'title_short' => 'Picture', 'upload' => 'Upload new picture', + 'aria_crop_selection' => [ + 'move' => 'Use the arrow keys to move the crop selection area', + 'top_left' => 'Use the arrow keys to move the top left drag handle to change the crop selection area', + 'top_right' => 'Use the arrow keys to move the top right drag handle to change the crop selection area', + 'bottom_left' => 'Use the arrow keys to move the bottom left drag handle to change the crop selection area', + 'bottom_right' => 'Use the arrow keys to move the bottom right drag handle to change the crop selection area', + ], ], 'last_login' => [ 'title' => 'Last login', diff --git a/lang/en/app.php b/lang/en/app.php index 3782829de..fb281070d 100644 --- a/lang/en/app.php +++ b/lang/en/app.php @@ -141,7 +141,9 @@ 'user' => 'User', 'user_name' => 'Name', 'users' => 'Users', - 'validation' => [ + 'file' => [ + 'allowed_formats' => 'Allowed file formats: :formats', + 'max_size' => 'Max. file size: :size', 'invalid_type' => 'The file type is not allowed.', 'too_large' => 'The selected file is too large.', ], diff --git a/lang/en/rooms.php b/lang/en/rooms.php index e7cb23dd6..25dc63e48 100644 --- a/lang/en/rooms.php +++ b/lang/en/rooms.php @@ -120,10 +120,8 @@ 'downloadable' => 'Downloadable files', 'use_in_meeting' => 'Files available in video conference', ], - 'formats' => 'Allowed file formats: :formats', 'nodata' => 'No files available', 'select_or_drag' => 'Select a file or drag and drop it here...', - 'size' => 'Max. file size: :size MB', 'sort' => [ 'filename' => 'Filename', 'uploaded_at' => 'Added', @@ -465,7 +463,7 @@ 'enabled' => 'Enabled', 'pause_image' => 'Pause image', 'pause_image_alt' => 'Pause image', - 'pause_image_format' => 'Format: PNG, JPEG, GIF, BMP; Resolution: 1920x1080px', + 'pause_image_resolution' => 'Resolution: 1920x1080px', 'title' => 'Streaming configuration', 'url' => 'RTMP(S) URL', ], diff --git a/package-lock.json b/package-lock.json index 4dcccf4eb..ca27cef69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "axios": "^1.14.0", "chart.js": "^4.5.1", "chartjs-adapter-date-fns": "^3.0.0", + "cropperjs": "^1.6.2", "date-fns": "^4.1.0", "dompurify": "^3.3.3", "dotenv": "^17.4.1", @@ -44,7 +45,6 @@ "vue": "^3.5", "vue-axe": "^3.1.2", "vue-chartjs": "^5.3.3", - "vue-cropperjs": "^5.0.0", "vue-i18n": "^11.3", "vue-multiselect": "^3.5.0", "vue-router": "^5.0.4" @@ -12397,18 +12397,6 @@ "vue": "^3.0.0-0 || ^2.7.0" } }, - "node_modules/vue-cropperjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/vue-cropperjs/-/vue-cropperjs-5.0.0.tgz", - "integrity": "sha512-RhnC8O33uRZNkn74aiHZwNHnBJOXWlS4P6gsRI0lw4cZlWjKSCywZI9oSI9POlIPI6OYv30jvnHMXGch85tw7w==", - "license": "MIT", - "dependencies": { - "cropperjs": "^1.5.6" - }, - "peerDependencies": { - "vue": ">=3.0.0" - } - }, "node_modules/vue-eslint-parser": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.4.0.tgz", diff --git a/package.json b/package.json index 09bbe7dd7..bd36a10ba 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "axios": "^1.14.0", "chart.js": "^4.5.1", "chartjs-adapter-date-fns": "^3.0.0", + "cropperjs": "^1.6.2", "date-fns": "^4.1.0", "dompurify": "^3.3.3", "dotenv": "^17.4.1", @@ -76,7 +77,6 @@ "vue": "^3.5", "vue-axe": "^3.1.2", "vue-chartjs": "^5.3.3", - "vue-cropperjs": "^5.0.0", "vue-i18n": "^11.3", "vue-multiselect": "^3.5.0", "vue-router": "^5.0.4" diff --git a/resources/css/app/_cropperjs.css b/resources/css/app/_cropperjs.css new file mode 100644 index 000000000..a133d432f --- /dev/null +++ b/resources/css/app/_cropperjs.css @@ -0,0 +1,48 @@ +@import "cropperjs/dist/cropper.css"; + +.cropper-container { + overflow: hidden; + + img { + display: block; + + /* This rule is very important, please don't ignore this */ + max-width: 100%; + } + + .cropper-view-box, + .cropper-face { + border-radius: 50%; + } + + .cropper-view-box { + outline: 0; + } + + .cropper-face { + background-color: transparent; + opacity: 1; + border: 1px dashed #fff; + } + + .cropper-line { + display: none; + } + + .cropper-point { + background-color: #fff; + border-radius: 50%; + border: 1px solid #767676; + opacity: 1; + width: 10px; + height: 10px; + } + + .cropper-point.point-n, + .cropper-point.point-w, + .cropper-point.point-s, + .cropper-point.point-e, + .cropper-point.point-se::before { + display: none; + } +} diff --git a/resources/css/app/app.css b/resources/css/app/app.css index 0cbdd1cea..ca04289ee 100644 --- a/resources/css/app/app.css +++ b/resources/css/app/app.css @@ -6,3 +6,4 @@ @import "./_profile-image.css"; @import "./_color-select.css"; @import "./_stretched-link.css"; +@import "./_cropperjs.css"; diff --git a/resources/js/components/AdminStreamingRoomTypeEditButton.vue b/resources/js/components/AdminStreamingRoomTypeEditButton.vue index df86e48ce..d091ed508 100644 --- a/resources/js/components/AdminStreamingRoomTypeEditButton.vue +++ b/resources/js/components/AdminStreamingRoomTypeEditButton.vue @@ -91,7 +91,7 @@ v-model:file="defaultPauseImage" v-model:file-deleted="defaultPauseImageDeleted" :disabled="disabled || isLoadingAction" - :max-file-size="5000000" + :max-file-size="5_000_000" :hide-url="true" show-delete :preview-alt="$t('rooms.streaming.config.pause_image_alt')" @@ -102,7 +102,9 @@ :url-error="formErrors.fieldError('default_pause_image')" :file-error="formErrors.fieldError('default_pause_image')" /> - {{ $t("rooms.streaming.config.pause_image_format") }} + {{ + $t("rooms.streaming.config.pause_image_resolution") + }} diff --git a/resources/js/components/RoomTabFilesUploadButton.vue b/resources/js/components/RoomTabFilesUploadButton.vue index c65a8018d..06041959a 100644 --- a/resources/js/components/RoomTabFilesUploadButton.vue +++ b/resources/js/components/RoomTabFilesUploadButton.vue @@ -48,12 +48,7 @@ type="file" class="sr-only" :disabled="disabled || isUploading" - :accept=" - '.' + - String(settingsStore.getSetting('bbb.file_mimes')) - .split(',') - .join(',.') - " + :accept="'.' + settingsStore.getSetting('bbb.file_mimes').join(',.')" @input="fileSelected" />
{{ - $t("rooms.files.formats", { - formats: settingsStore - .getSetting("bbb.file_mimes") - .replaceAll(",", ", "), + $t("app.file.allowed_formats", { + formats: settingsStore.getSetting("bbb.file_mimes").join(", "), }) }}
{{ - $t("rooms.files.size", { - size: settingsStore.getSetting("bbb.max_filesize"), + $t("app.file.max_size", { + size: fileHelpers.fileSize( + settingsStore.getSetting("bbb.max_filesize") * 1_000_000, + ), }) }}
@@ -119,6 +114,9 @@ import { useApi } from "../composables/useApi.js"; import env from "../env.js"; import { useI18n } from "vue-i18n"; import { useSettingsStore } from "../stores/settings.js"; +import { useFileHelpers } from "../composables/useFileHelpers.js"; + +const fileHelpers = useFileHelpers(); const props = defineProps({ roomId: { @@ -257,7 +255,7 @@ function uploadFile(file) { reset(); if (error.response) { if (error.response.status === env.HTTP_PAYLOAD_TOO_LARGE) { - formErrors.set({ file: [t("app.validation.too_large")] }); + formErrors.set({ file: [t("app.file.too_large")] }); return; } if (error.response.status === env.HTTP_UNPROCESSABLE_ENTITY) { diff --git a/resources/js/components/RoomTabStreamingConfigButton.vue b/resources/js/components/RoomTabStreamingConfigButton.vue index 9a34fd515..28d0f0a14 100644 --- a/resources/js/components/RoomTabStreamingConfigButton.vue +++ b/resources/js/components/RoomTabStreamingConfigButton.vue @@ -137,7 +137,7 @@ v-model:file="streamingPauseImageFile" v-model:file-deleted="streamingPauseImageDeleted" :disabled="formDisabled" - :max-file-size="5000000" + :max-file-size="5_000_000" :hide-url="true" show-delete :show-view="false" @@ -147,7 +147,9 @@ :file-invalid="formErrors.fieldInvalid('pause_image')" :file-error="formErrors.fieldError('pause_image')" /> - {{ $t("rooms.streaming.config.pause_image_format") }} + {{ + $t("rooms.streaming.config.pause_image_resolution") + }}
diff --git a/resources/js/components/SettingsFileSelector.vue b/resources/js/components/SettingsFileSelector.vue index c30d576d9..b7f47705d 100644 --- a/resources/js/components/SettingsFileSelector.vue +++ b/resources/js/components/SettingsFileSelector.vue @@ -56,19 +56,33 @@
+ {{ + $t("app.file.allowed_formats", { + formats: allowedExtensions.join(", "), + }) + }}
{{ + $t("app.file.max_size", { + size: fileHelpers.fileSize(maxFileSize), + }) + }}
diff --git a/resources/js/components/UserTabProfile.vue b/resources/js/components/UserTabProfile.vue index 51c74f685..59b1a6631 100644 --- a/resources/js/components/UserTabProfile.vue +++ b/resources/js/components/UserTabProfile.vue @@ -250,7 +250,7 @@ function save() { formData.append("image", ""); } else if (croppedImageBlob.value != null) { // cropped image - formData.append("image", croppedImageBlob.value, "image.png"); + formData.append("image", croppedImageBlob.value, "image.jpg"); } formErrors.clear(); diff --git a/resources/js/composables/useFileHelpers.js b/resources/js/composables/useFileHelpers.js new file mode 100644 index 000000000..e80712aaa --- /dev/null +++ b/resources/js/composables/useFileHelpers.js @@ -0,0 +1,38 @@ +export function useFileHelpers() { + return new FileHelpers(); +} + +class FileHelpers { + /** + * Parse file size to human-readable format using SI suffixes (base 10) + * @param sizeInBytes File size in bytes + */ + fileSize(sizeInBytes) { + const bytes = Number(sizeInBytes); + + // Validate input: must be a finite number greater than zero + if (!Number.isFinite(bytes) || bytes <= 0) return "0 B"; + + // SI unit suffixes in ascending order (base 10: 1 kB = 1000 B) + const units = ["B", "kB", "MB", "GB"]; + + // Determine the appropriate unit exponent (0 = B, 1 = kB, 2 = MB, …) + // log10(sizeInBytes) / 3 gives the base-1000 exponent; clamped to the available units + let exponent = Math.min( + Math.floor(Math.log10(sizeInBytes) / 3), + units.length - 1, + ); + + // Scale the value down to the chosen unit and round to 2 decimal places + let value = Math.round((bytes / Math.pow(1000, exponent)) * 100) / 100; + + // If the value rounds up to 1000, we need to move to the next unit + if (value === 1000 && exponent < units.length - 1) { + exponent += 1; + value = 1; + } + + // Return the rounded value with its corresponding SI unit suffix + return `${value} ${units[exponent]}`; + } +} diff --git a/resources/js/views/AdminSettings.vue b/resources/js/views/AdminSettings.vue index 1fb9e1d74..ed750d314 100644 --- a/resources/js/views/AdminSettings.vue +++ b/resources/js/views/AdminSettings.vue @@ -487,7 +487,7 @@ v-model:file-deleted="themeCustomCssDeleted" :disabled="disabled" :readonly="viewOnly" - :max-file-size="500000" + :max-file-size="500_000" show-delete :allowed-extensions="['css']" :file-invalid="formErrors.fieldInvalid('theme_custom_css')" @@ -1443,7 +1443,7 @@ v-model:file-deleted="bbbStyleDeleted" :disabled="disabled" :readonly="viewOnly" - :max-file-size="500000" + :max-file-size="500_000" show-delete :allowed-extensions="['css']" :file-invalid="formErrors.fieldInvalid('bbb_style')" @@ -1469,13 +1469,11 @@ :disabled="disabled" :readonly="viewOnly" :max-file-size=" - settingsStore.getSetting('bbb.max_filesize') * 1000000 + settingsStore.getSetting('bbb.max_filesize') * 1_000_000 " show-delete :allowed-extensions=" - String(settingsStore.getSetting('bbb.file_mimes')).split( - ',', - ) + settingsStore.getSetting('bbb.file_mimes') " :file-invalid=" formErrors.fieldInvalid('bbb_default_presentation') diff --git a/resources/js/views/AdminStreamingSettings.vue b/resources/js/views/AdminStreamingSettings.vue index 16a8e5b0e..c75dd6811 100644 --- a/resources/js/views/AdminStreamingSettings.vue +++ b/resources/js/views/AdminStreamingSettings.vue @@ -27,14 +27,14 @@ v-model:file-deleted="defaultPauseImageDeleted" :disabled="disabled" :readonly="viewOnly" - :max-file-size="5000000" + :max-file-size="5_000_000" show-delete :allowed-extensions="['jpg', 'jpeg', 'png', 'gif']" :file-invalid="formErrors.fieldInvalid('default_pause_image')" :file-error="formErrors.fieldError('default_pause_image')" /> {{ - $t("rooms.streaming.config.pause_image_format") + $t("rooms.streaming.config.pause_image_resolution") }} @@ -55,7 +55,7 @@ v-model:file-deleted="cssFileDeleted" :disabled="disabled" :readonly="viewOnly" - :max-file-size="500000" + :max-file-size="5_000_000" show-delete :allowed-extensions="['css']" :file-invalid="formErrors.fieldInvalid('css_file')" diff --git a/resources/js/views/AdminUsersNew.vue b/resources/js/views/AdminUsersNew.vue index 3dc2526f5..879dcd4b2 100644 --- a/resources/js/views/AdminUsersNew.vue +++ b/resources/js/views/AdminUsersNew.vue @@ -215,7 +215,6 @@