Here's the relevant frontend source code for the file upload picker, if anyone's wondering (webpack://src/components/exam/submissions/FileInput.js):
<input
type="file"
ref={inputRef}
name="fileupload"
disabled={disabled}
accept={EXTENSIONS[type]}
data-cb-element="no-cb"
onChange={async e => {
const [file] = Object.keys(e.target.files).map(key => e.target.files[key])
const fileSizeInMb = file.size/(CONVERSION_BASE*CONVERSION_BASE)
const fileSizeInKb = file.size/CONVERSION_BASE
const split = file.name.split('.')
const fileType = split[split.length-1].toLowerCase()
const extensions = EXTENSIONS[type].split(', ')
const isAllowedExtension = extensions.includes(`.${fileType}`)
const alreadyExists = files.find(f => f.name === file.name && f.size === file.size && f.lastModified === file.lastModified)
let error
if (alreadyExists)
error = { title: 'You have already uploaded a file with the same name.', details: 'Please attach a different file.' }
else if (!isAllowedExtension)
error = { title: 'This file type is not acceptable.', details: 'Please check the requirements, save your file in one of the accepted formats, and resubmit.' }
else if (fileSizeInKb <= minSize)
error = { title: 'Your file is too small.', details: 'Please check the file-size requirements, save a larger version, and resubmit.' }
else if (fileSizeInMb > maxSize)
error = { title: 'Your file is too big.', details: 'Please check the file-size requirements, save a smaller version, and resubmit. ' }
if (error)
await setError(<><strong>{error.title}</strong> {error.details}</>)
else {
await updateFiles(file)
setError(null)
}
inputRef.current.value = null
}}
/>
The EXTENSIONS variable is defined here: export const EXTENSIONS = {
[TYPE_DOC]: '.txt, .doc, .docx, .pdf, .odt',
[TYPE_PHOTO]: '.png, .jpg, .jpeg',
[TYPE_AUDIO]: '.m4a, .mp3, .wav, .ogg'
}
replies(4):