From 2f1da3e9e83ef524be63b33e3ede2553f79740c2 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Van Date: Mon, 24 Nov 2025 08:55:52 +0000 Subject: [PATCH] MDL-86616 filepicker: link restrictions to input via aria-describedby --- lib/form/filemanager.php | 8 +++- lib/form/filepicker.php | 8 +++- question/type/essay/renderer.php | 6 ++- repository/filepicker.js | 70 +++++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 4 deletions(-) diff --git a/lib/form/filemanager.php b/lib/form/filemanager.php index cc285cdd871..dfe37f27384 100644 --- a/lib/form/filemanager.php +++ b/lib/form/filemanager.php @@ -290,7 +290,13 @@ class MoodleQuickForm_filemanager extends HTML_QuickForm_element implements temp $html .= html_writer::empty_tag('input', array('value' => $draftitemid, 'name' => $elname, 'type' => 'hidden', 'id' => $id)); if (!empty($options->accepted_types) && $options->accepted_types != '*') { - $html .= html_writer::tag('div', get_string('filesofthesetypes', 'form'), ['class' => 'mt-1']); + $html .= html_writer::tag( + 'div', + get_string('filesofthesetypes', 'form'), + [ + 'class' => 'filetypes-descriptions-intro mt-1', + ], + ); $util = new \core_form\filetypes_util(); $filetypes = $options->accepted_types; $filetypedescriptions = $util->describe_file_types($filetypes); diff --git a/lib/form/filepicker.php b/lib/form/filepicker.php index a1da8e8dcf4..32c3285bbf9 100644 --- a/lib/form/filepicker.php +++ b/lib/form/filepicker.php @@ -185,7 +185,13 @@ class MoodleQuickForm_filepicker extends HTML_QuickForm_input implements templat $html .= ''; if (!empty($args->accepted_types) && $args->accepted_types != '*') { - $html .= html_writer::tag('div', get_string('filesofthesetypes', 'form'), ['class' => 'mt-1']); + $html .= html_writer::tag( + 'div', + get_string('filesofthesetypes', 'form'), + [ + 'class' => 'filetypes-descriptions-intro mt-1', + ], + ); $util = new \core_form\filetypes_util(); $filetypedescriptions = $util->describe_file_types($args->accepted_types); $html .= $OUTPUT->render_from_template('core_form/filetypes-descriptions', $filetypedescriptions); diff --git a/question/type/essay/renderer.php b/question/type/essay/renderer.php index 62742eb51b5..4b5dbf0e5eb 100644 --- a/question/type/essay/renderer.php +++ b/question/type/essay/renderer.php @@ -175,7 +175,11 @@ class qtype_essay_renderer extends qtype_renderer { $text = ''; if (!empty($qa->get_question()->filetypeslist)) { - $text = html_writer::tag('p', get_string('acceptedfiletypes', 'qtype_essay')); + $text = html_writer::tag( + 'div', + get_string('acceptedfiletypes', 'qtype_essay'), + ['class' => 'filetypes-descriptions-intro mt-1'], + ); $filetypesutil = new \core_form\filetypes_util(); $filetypes = $qa->get_question()->filetypeslist; $filetypedescriptions = $filetypesutil->describe_file_types($filetypes); diff --git a/repository/filepicker.js b/repository/filepicker.js index 9b81d511faa..ecd649cb17a 100644 --- a/repository/filepicker.js +++ b/repository/filepicker.js @@ -1892,7 +1892,75 @@ M.core_filepicker.init = function(Y, options) { node.all('label').set('for', node.one('input,select').generateID()); }); content.one('form').set('id', id); - content.one('.fp-file input').set('name', 'repo_upload_file'); + // Define element IDs. + const ids = { + restrictionsSpan: 'fp-restrictions-span-' + client_id, + filetypesDescriptions: 'form-filetypes-descriptions-' + client_id, + filetypesDescriptionsIntro: 'filetypes-descriptions-intro-' + client_id, + }; + + /** + * Sets unique IDs for file type restriction and description elements within the file picker or file manager wrapper. + * + * @param {Y.Node} wrapper - The YUI Node wrapper element to search within (e.g., filepicker or filemanager wrapper). + * @param {Object} ids - An object containing the IDs to assign to relevant elements. + * - restrictionsSpan: ID for the restrictions span element. + * - filetypesDescriptions: ID for the file types descriptions element. + * - filetypesDescriptionsIntro: ID for the file types descriptions intro element. + */ + function setFileTypeIds(wrapper, ids) { + if (!wrapper) { + return { + hasRestrictionSpan: false, + hasFiletypesDescriptions: false, + hasFiletypesDescriptionsIntro: false, + }; + } + const parent = wrapper.get('parentNode'); + const restrictionSpan = wrapper.one('.fp-restrictions span'); + if (restrictionSpan) { + restrictionSpan.set('id', ids.restrictionsSpan); + } + const filetypesDescriptions = parent.one('.form-filetypes-descriptions'); + if (filetypesDescriptions) { + filetypesDescriptions.set('id', ids.filetypesDescriptions); + } + const filetypesDescriptionsIntro = parent.one('.filetypes-descriptions-intro'); + if (filetypesDescriptionsIntro) { + filetypesDescriptionsIntro.set('id', ids.filetypesDescriptionsIntro); + } + return { + hasRestrictionSpan: !!restrictionSpan, + hasFiletypesDescriptions: !!filetypesDescriptions, + hasFiletypesDescriptionsIntro: !!filetypesDescriptionsIntro, + }; + } + + const filepickerIdsSet = setFileTypeIds(Y.one('#filepicker-wrapper-' + client_id), ids); + const filemanagerIdsSet = setFileTypeIds(Y.one('#filemanager-' + client_id), ids); + + let idsToDescribe = ''; + // Set value of aria-describedby. + if (filepickerIdsSet.hasRestrictionSpan || filemanagerIdsSet.hasRestrictionSpan) { + idsToDescribe += ids.restrictionsSpan; + } + if (filepickerIdsSet.hasFiletypesDescriptionsIntro || filemanagerIdsSet.hasFiletypesDescriptionsIntro) { + if (idsToDescribe.length > 0) { + idsToDescribe += ' '; + } + idsToDescribe += ids.filetypesDescriptionsIntro; + } + if (filepickerIdsSet.hasFiletypesDescriptions || filemanagerIdsSet.hasFiletypesDescriptions) { + if (idsToDescribe.length > 0) { + idsToDescribe += ' '; + } + idsToDescribe += ids.filetypesDescriptions; + } + + content + .one('.fp-file input') + .set('name', 'repo_upload_file') + .set('aria-describedby', idsToDescribe); if (data.upload.label && content.one('.fp-file label')) { content.one('.fp-file label').setContent(data.upload.label); }