Merge branch 'MDL-81608-501-2' of https://github.com/junpataleta/moodle into MOODLE_501_STABLE

This commit is contained in:
Mihail Geshoski
2026-03-13 09:53:55 +08:00
18 changed files with 156 additions and 97 deletions
+1
View File
@@ -826,6 +826,7 @@ M.util.add_spinner = function(Y, node) {
var spinner = Y.Node.create('<img />')
.setAttribute('src', M.util.image_url(WAITICON.pix, WAITICON.component))
.setAttribute('alt', M.util.get_string('loading', 'core'))
.addClass('spinner icon')
.hide();
@@ -183,6 +183,7 @@ Y.extend(DRAGDROP, Y.Base, {
var dragelement = Y.Node.create('<span></span>')
.addClass(classname)
.setAttribute('title', title)
.setAttribute('aria-label', title)
.setAttribute('tabIndex', 0)
.setAttribute('data-draggroups', this.groups)
.setAttribute('role', 'button');
File diff suppressed because one or more lines are too long
@@ -183,6 +183,7 @@ Y.extend(DRAGDROP, Y.Base, {
var dragelement = Y.Node.create('<span></span>')
.addClass(classname)
.setAttribute('title', title)
.setAttribute('aria-label', title)
.setAttribute('tabIndex', 0)
.setAttribute('data-draggroups', this.groups)
.setAttribute('role', 'button');
+1
View File
@@ -181,6 +181,7 @@ Y.extend(DRAGDROP, Y.Base, {
var dragelement = Y.Node.create('<span></span>')
.addClass(classname)
.setAttribute('title', title)
.setAttribute('aria-label', title)
.setAttribute('tabIndex', 0)
.setAttribute('data-draggroups', this.groups)
.setAttribute('role', 'button');
@@ -101,8 +101,11 @@ class edit_renderer extends \plugin_renderer_base {
if ($structure->is_last_section($section)) {
$output .= \html_writer::start_div('last-add-menu');
$output .= html_writer::tag('span', $this->add_menu_actions($structure, 0,
$pageurl, $contexts, $pagevars), ['class' => 'add-menu-outer']);
$output .= html_writer::tag(
'span',
$this->add_menu_actions($structure, 0, $pageurl, $contexts, $pagevars),
['class' => 'add-menu-outer pe-3']
);
$output .= \html_writer::end_div();
}
@@ -588,9 +591,11 @@ class edit_renderer extends \plugin_renderer_base {
if ($structure->is_first_slot_on_page($slot)) {
// Add the add-menu at the page level.
$addmenu = html_writer::tag('span', $this->add_menu_actions($structure,
$pagenumber, $pageurl, $contexts, $pagevars),
['class' => 'add-menu-outer']);
$addmenu = html_writer::tag(
'span',
$this->add_menu_actions($structure, $pagenumber, $pageurl, $contexts, $pagevars),
['class' => 'add-menu-outer pe-2']
);
$addquestionform = $this->add_question_form($structure,
$pagenumber, $pageurl, $pagevars);
@@ -889,9 +894,15 @@ class edit_renderer extends \plugin_renderer_base {
* @return string The markup for the move action.
*/
public function question_move_icon(structure $structure, $slot) {
return html_writer::link(new \moodle_url('#'),
$this->pix_icon('i/dragdrop', get_string('move'), 'moodle', ['class' => 'iconsmall', 'title' => '']),
['class' => 'editing_move', 'data-action' => 'move']
$slotnumber = $structure->get_displayed_number_for_slot($slot);
return html_writer::link(
new \moodle_url('#'),
$this->pix_icon('i/dragdrop', '', 'moodle', ['class' => 'iconsmall']),
[
'class' => 'editing_move',
'data-action' => 'move',
'aria-label' => get_string('movequestionnumber', 'quiz', $slotnumber),
]
);
}
@@ -983,13 +994,14 @@ class edit_renderer extends \plugin_renderer_base {
$url = new \moodle_url('repaginate.php', ['quizid' => $structure->get_quizid(),
'slot' => $slot, 'repag' => $insertpagebreak ? 2 : 1, 'sesskey' => sesskey()]);
$slotname = $structure->get_displayed_number_for_slot($slot);
if ($insertpagebreak) {
$title = get_string('addpagebreak', 'quiz');
$image = $this->image_icon('e/insert_page_break', $title);
$title = get_string('addpagebreakafter', 'quiz', $slotname);
$image = $this->image_icon('e/insert_page_break', '');
$action = 'addpagebreak';
} else {
$title = get_string('removepagebreak', 'quiz');
$image = $this->image_icon('e/remove_page_break', $title);
$title = get_string('removepagebreakafter', 'quiz', $slotname);
$image = $this->image_icon('e/remove_page_break', '');
$action = 'removepagebreak';
}
@@ -998,9 +1010,22 @@ class edit_renderer extends \plugin_renderer_base {
if (!$structure->can_be_edited()) {
$disabled = 'disabled';
}
return html_writer::span($this->action_link($url, $image, null, ['title' => $title,
'class' => 'page_split_join cm-edit-action', 'disabled' => $disabled, 'data-action' => $action]),
'page_split_join_wrapper');
return html_writer::span(
$this->action_link(
$url,
$image,
null,
[
'title' => $title,
'aria-label' => $title,
'class' => 'page_split_join cm-edit-action btn btn-sm icon-no-margin',
'disabled' => $disabled,
'data-action' => $action,
'role' => 'button',
]
),
'page_split_join_wrapper'
);
}
/**
@@ -1018,13 +1043,11 @@ class edit_renderer extends \plugin_renderer_base {
];
if ($structure->is_question_dependent_on_previous_slot($slot)) {
$title = get_string('questiondependencyremove', 'quiz', $a);
$image = $this->pix_icon('t/locked', get_string('questiondependsonprevious', 'quiz'),
'moodle', ['title' => '']);
$image = $this->pix_icon('t/locked', '');
$action = 'removedependency';
} else {
$title = get_string('questiondependencyadd', 'quiz', $a);
$image = $this->pix_icon('t/unlocked', get_string('questiondependencyfree', 'quiz'),
'moodle', ['title' => '']);
$image = $this->pix_icon('t/unlocked', '');
$action = 'adddependency';
}
@@ -1037,9 +1060,22 @@ class edit_renderer extends \plugin_renderer_base {
if (!$structure->can_question_depend_on_previous_slot($slot)) {
$extraclass = ' question_dependency_cannot_depend';
}
return html_writer::span($this->action_link('#', $image, null, ['title' => $title,
'class' => 'cm-edit-action', 'disabled' => $disabled, 'data-action' => $action]),
'question_dependency_wrapper' . $extraclass);
return html_writer::span(
$this->action_link(
'#',
$image,
null,
[
'title' => $title,
'aria-label' => $title,
'class' => 'cm-edit-action btn btn-link btn-sm icon-no-margin',
'disabled' => $disabled,
'data-action' => $action,
'role' => 'button',
]
),
'question_dependency_wrapper' . $extraclass
);
}
/**
@@ -1303,7 +1339,7 @@ class edit_renderer extends \plugin_renderer_base {
], 'moodle');
$this->page->requires->strings_for_js([
'addpagebreak',
'addpagebreakafter',
'cannotremoveallsectionslots',
'cannotremoveslots',
'confirmremovesectionheading',
@@ -1314,7 +1350,7 @@ class edit_renderer extends \plugin_renderer_base {
'sectionheadingedit',
'sectionheadingremove',
'sectionnoname',
'removepagebreak',
'removepagebreakafter',
'questiondependencyadd',
'questiondependencyfree',
'questiondependencyremove',
+3
View File
@@ -44,6 +44,7 @@ $string['addnewpagesafterselected'] = 'Add new pages after selected questions';
$string['addnewquestionsqbank'] = 'Add questions to the category {$a->catname}: {$a->link}';
$string['addnewuseroverride'] = 'Add user override';
$string['addpagebreak'] = 'Add page break';
$string['addpagebreakafter'] = 'Add page break after question {$a}';
$string['addpagehere'] = 'Add page here';
$string['addquestion'] = 'Add question';
$string['addquestionfrombankatend'] = 'Add from the question bank at the end';
@@ -583,6 +584,7 @@ Quizzes may be used
* For self-assessment';
$string['modulename_link'] = 'mod/quiz/view';
$string['modulenameplural'] = 'Quizzes';
$string['movequestionnumber'] = 'Move question: {$a}';
$string['moveselectedonpage'] = 'Move selected questions to page: {$a}';
$string['multichoice'] = 'Multiple choice';
$string['multipleanswers'] = 'Choose at least one answer.';
@@ -901,6 +903,7 @@ $string['removeallquizattempts'] = 'All quiz attempts';
$string['removealluseroverrides'] = 'All user overrides';
$string['removeemptypage'] = 'Remove empty page';
$string['removepagebreak'] = 'Remove page break';
$string['removepagebreakafter'] = 'Remove page break after question {$a}';
$string['removeselected'] = 'Remove selected';
$string['rename'] = 'Rename';
$string['renderingserverconnectfailed'] = 'The server {$a} failed to process an RQP request. Check that the URL is correct.';
+8 -5
View File
@@ -615,7 +615,7 @@ table.quizreviewsummary td.cell {
#page-mod-quiz-edit ul.section {
margin: 0;
padding: 0 20px;
padding: 0 38px;
}
#page-mod-quiz-edit ul.slots {
@@ -688,8 +688,11 @@ table.quizreviewsummary td.cell {
#page-mod-quiz-edit ul.slots li.section li.activity .page_split_join_wrapper {
position: absolute;
left: -20px;
bottom: -11px;
left: -38px;
bottom: -18px;
.icon {
width: auto;
}
}
#page-mod-quiz-edit ul.slots li.section li.activity .instancemaxmark.decimalplaces_0 {
@@ -742,8 +745,8 @@ table.quizreviewsummary td.cell {
#page-mod-quiz-edit ul.slots li.section li.activity .question_dependency_wrapper {
position: absolute;
top: -1em;
right: -20px;
top: -1.25em;
right: -38px;
}
#page-mod-quiz-edit ul.slots li.section li.activity .question_dependency_wrapper.question_dependency_cannot_depend {
@@ -565,7 +565,7 @@ class behat_mod_quiz extends behat_question_base {
*/
protected function get_xpath_page_break_icon_after_question($addorremoves, $questionname) {
return "//li[contains(@class, 'slot') and contains(., '" . $this->escape($questionname) .
"')]//a[contains(@class, 'page_split_join') and @title = '" . $addorremoves . " page break']";
"')]//a[contains(@class, 'page_split_join') and contains(@aria-label, '$addorremoves page break')]";
}
/**
@@ -51,7 +51,7 @@ Feature: Edit quizzes where some questions require the previous one to have been
| TF1 | 1 | 0 |
| TF2 | 1 | 1 |
And I am on the "Quiz 1" "mod_quiz > Edit" page
Then "This question cannot be attempted until the previous question has been completed." "link" should be visible
Then "Question 2 cannot be attempted until the previous question 1 has been completed" "button" should be visible
@javascript
Scenario: A question can depend on a random question
@@ -67,9 +67,9 @@ Feature: Edit quizzes where some questions require the previous one to have been
| Random (Test questions) | 1 | 0 |
| TF1 | 1 | 1 |
And I am on the "Quiz 1" "mod_quiz > Edit" page
Then "This question cannot be attempted until the previous question has been completed." "link" should be visible
Then "Question 2 cannot be attempted until the previous question 1 has been completed" "button" should be visible
@javascript
@javascript @accessibility
Scenario: The second question can be set to depend on the first
Given the following "activities" exist:
| activity | name | intro | course | idnumber | preferredbehaviour |
@@ -85,9 +85,10 @@ Feature: Edit quizzes where some questions require the previous one to have been
| TF2 | 1 | 0 |
| TF3 | 1 | 0 |
And I am on the "Quiz 1" "mod_quiz > Edit" page
When I follow "No restriction on when question 2 can be attempted Click to change"
Then "Question 2 cannot be attempted until the previous question 1 has been completed Click to change" "link" should be visible
And "No restriction on when question 3 can be attempted Click to change" "link" should be visible
When I click on "No restriction on when question 2 can be attempted Click to change" "button"
Then "Question 2 cannot be attempted until the previous question 1 has been completed Click to change" "button" should be visible
And "No restriction on when question 3 can be attempted Click to change" "button" should be visible
And the "region-main" "region" should meet accessibility standards with "best-practice" extra tests
@javascript
Scenario: A question that did depend on the previous can be un-linked
@@ -105,9 +106,9 @@ Feature: Edit quizzes where some questions require the previous one to have been
| TF2 | 1 | 1 |
| TF3 | 1 | 1 |
And I am on the "Quiz 1" "mod_quiz > Edit" page
When I follow "Question 3 cannot be attempted until the previous question 2 has been completed Click to change"
Then "Question 2 cannot be attempted until the previous question 1 has been completed Click to change" "link" should be visible
And "No restriction on when question 3 can be attempted Click to change" "link" should be visible
When I click on "Question 3 cannot be attempted until the previous question 2 has been completed Click to change" "button"
Then "Question 2 cannot be attempted until the previous question 1 has been completed Click to change" "button" should be visible
And "No restriction on when question 3 can be attempted Click to change" "button" should be visible
@javascript
Scenario: Question dependency cannot apply to deferred feedback quizzes so UI is hidden
@@ -232,6 +233,6 @@ Feature: Edit quizzes where some questions require the previous one to have been
| TF3 | 1 | 1 |
And I am on the "Quiz 1" "mod_quiz > Edit" page
When I move "TF1" to "After Question 3" in the quiz by clicking the move icon
Then "Question 2 cannot be attempted until the previous question 1 has been completed Click to change" "link" should be visible
And "No restriction on when question 3 can be attempted Click to change" "link" should be visible
Then "Question 2 cannot be attempted until the previous question 1 has been completed Click to change" "button" should be visible
And "No restriction on when question 3 can be attempted Click to change" "button" should be visible
And "be attempted" "link" in the "TF2" "list_item" should not be visible
@@ -384,8 +384,9 @@ Y.extend(DRAGRESOURCE, M.core.dragdrop, {
// Replace move icons
var move = resourcesnode.one('a.' + CSS.EDITINGMOVE);
if (move) {
var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'),
CSS.EDITINGMOVE, CSS.ICONCLASS, true);
var slotName = move.getAttribute('aria-label');
var dragHandleClass = 'btn px-1 icon-no-margin ' + CSS.EDITINGMOVE;
var resourcedraghandle = this.get_drag_handle(slotName, dragHandleClass, CSS.ICONCLASS);
move.replace(resourcedraghandle);
}
}, this);
File diff suppressed because one or more lines are too long
@@ -378,8 +378,9 @@ Y.extend(DRAGRESOURCE, M.core.dragdrop, {
// Replace move icons
var move = resourcesnode.one('a.' + CSS.EDITINGMOVE);
if (move) {
var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'),
CSS.EDITINGMOVE, CSS.ICONCLASS, true);
var slotName = move.getAttribute('aria-label');
var dragHandleClass = 'btn px-1 icon-no-margin ' + CSS.EDITINGMOVE;
var resourcedraghandle = this.get_drag_handle(slotName, dragHandleClass, CSS.ICONCLASS);
move.replace(resourcedraghandle);
}
}, this);
@@ -335,21 +335,24 @@ Y.Moodle.mod_quiz.util.slot = {
// Get the correct title.
var action = '';
var iconname = '';
var actionStr = '';
var slotNumber = this.getNumber(slot);
if (Y.Moodle.mod_quiz.util.page.isPage(nextitem)) {
action = 'removepagebreak';
actionStr = 'removepagebreakafter';
iconname = 'e/remove_page_break';
} else {
action = 'addpagebreak';
actionStr = 'addpagebreakafter';
iconname = 'e/insert_page_break';
}
// Update the link and image titles
pagebreaklink.set('title', M.util.get_string(action, 'quiz'));
pagebreaklink.set('title', M.util.get_string(actionStr, 'quiz', slotNumber));
pagebreaklink.set('aria-label', M.util.get_string(actionStr, 'quiz', slotNumber));
pagebreaklink.setData('action', action);
// Update the image title.
var icon = pagebreaklink.one(this.SELECTORS.ICON);
icon.set('title', M.util.get_string(action, 'quiz'));
icon.set('alt', M.util.get_string(action, 'quiz'));
// Update the image src.
icon.set('src', M.util.image_url(iconname));
@@ -417,27 +420,27 @@ Y.Moodle.mod_quiz.util.slot = {
requiresprevious = link.getData('action') === 'removedependency';
}
var iconname = '';
if (requiresprevious) {
link.set('title', M.util.get_string('questiondependencyremove', 'quiz', a));
link.set('aria-label', M.util.get_string('questiondependencyremove', 'quiz', a));
link.setData('action', 'removedependency');
window.require(['core/templates'], function(Templates) {
Templates.renderPix('t/locked', 'core', M.util.get_string('questiondependsonprevious', 'quiz')).then(
function(html) {
icon.replace(html);
}
);
});
iconname = 't/locked';
} else {
link.set('title', M.util.get_string('questiondependencyadd', 'quiz', a));
link.set('aria-label', M.util.get_string('questiondependencyadd', 'quiz', a));
link.setData('action', 'adddependency');
window.require(['core/templates'], function(Templates) {
Templates.renderPix('t/unlocked', 'core', M.util.get_string('questiondependencyfree', 'quiz')).then(
iconname = 't/unlocked';
}
window.require(['core/templates', 'core/notification'], function(Templates, Notification) {
Templates.renderPix(iconname, 'core', '')
.then(
function(html) {
icon.replace(html);
return;
}
);
});
}
).catch(Notification.exception);
});
}
};
File diff suppressed because one or more lines are too long
@@ -335,21 +335,24 @@ Y.Moodle.mod_quiz.util.slot = {
// Get the correct title.
var action = '';
var iconname = '';
var actionStr = '';
var slotNumber = this.getNumber(slot);
if (Y.Moodle.mod_quiz.util.page.isPage(nextitem)) {
action = 'removepagebreak';
actionStr = 'removepagebreakafter';
iconname = 'e/remove_page_break';
} else {
action = 'addpagebreak';
actionStr = 'addpagebreakafter';
iconname = 'e/insert_page_break';
}
// Update the link and image titles
pagebreaklink.set('title', M.util.get_string(action, 'quiz'));
pagebreaklink.set('title', M.util.get_string(actionStr, 'quiz', slotNumber));
pagebreaklink.set('aria-label', M.util.get_string(actionStr, 'quiz', slotNumber));
pagebreaklink.setData('action', action);
// Update the image title.
var icon = pagebreaklink.one(this.SELECTORS.ICON);
icon.set('title', M.util.get_string(action, 'quiz'));
icon.set('alt', M.util.get_string(action, 'quiz'));
// Update the image src.
icon.set('src', M.util.image_url(iconname));
@@ -417,27 +420,27 @@ Y.Moodle.mod_quiz.util.slot = {
requiresprevious = link.getData('action') === 'removedependency';
}
var iconname = '';
if (requiresprevious) {
link.set('title', M.util.get_string('questiondependencyremove', 'quiz', a));
link.set('aria-label', M.util.get_string('questiondependencyremove', 'quiz', a));
link.setData('action', 'removedependency');
window.require(['core/templates'], function(Templates) {
Templates.renderPix('t/locked', 'core', M.util.get_string('questiondependsonprevious', 'quiz')).then(
function(html) {
icon.replace(html);
}
);
});
iconname = 't/locked';
} else {
link.set('title', M.util.get_string('questiondependencyadd', 'quiz', a));
link.set('aria-label', M.util.get_string('questiondependencyadd', 'quiz', a));
link.setData('action', 'adddependency');
window.require(['core/templates'], function(Templates) {
Templates.renderPix('t/unlocked', 'core', M.util.get_string('questiondependencyfree', 'quiz')).then(
iconname = 't/unlocked';
}
window.require(['core/templates', 'core/notification'], function(Templates, Notification) {
Templates.renderPix(iconname, 'core', '')
.then(
function(html) {
icon.replace(html);
return;
}
);
});
}
).catch(Notification.exception);
});
}
};
+3 -2
View File
@@ -83,8 +83,9 @@ Y.extend(DRAGRESOURCE, M.core.dragdrop, {
// Replace move icons
var move = resourcesnode.one('a.' + CSS.EDITINGMOVE);
if (move) {
var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'),
CSS.EDITINGMOVE, CSS.ICONCLASS, true);
var slotName = move.getAttribute('aria-label');
var dragHandleClass = 'btn px-1 icon-no-margin ' + CSS.EDITINGMOVE;
var resourcedraghandle = this.get_drag_handle(slotName, dragHandleClass, CSS.ICONCLASS);
move.replace(resourcedraghandle);
}
}, this);
+18 -15
View File
@@ -333,21 +333,24 @@ Y.Moodle.mod_quiz.util.slot = {
// Get the correct title.
var action = '';
var iconname = '';
var actionStr = '';
var slotNumber = this.getNumber(slot);
if (Y.Moodle.mod_quiz.util.page.isPage(nextitem)) {
action = 'removepagebreak';
actionStr = 'removepagebreakafter';
iconname = 'e/remove_page_break';
} else {
action = 'addpagebreak';
actionStr = 'addpagebreakafter';
iconname = 'e/insert_page_break';
}
// Update the link and image titles
pagebreaklink.set('title', M.util.get_string(action, 'quiz'));
pagebreaklink.set('title', M.util.get_string(actionStr, 'quiz', slotNumber));
pagebreaklink.set('aria-label', M.util.get_string(actionStr, 'quiz', slotNumber));
pagebreaklink.setData('action', action);
// Update the image title.
var icon = pagebreaklink.one(this.SELECTORS.ICON);
icon.set('title', M.util.get_string(action, 'quiz'));
icon.set('alt', M.util.get_string(action, 'quiz'));
// Update the image src.
icon.set('src', M.util.image_url(iconname));
@@ -415,26 +418,26 @@ Y.Moodle.mod_quiz.util.slot = {
requiresprevious = link.getData('action') === 'removedependency';
}
var iconname = '';
if (requiresprevious) {
link.set('title', M.util.get_string('questiondependencyremove', 'quiz', a));
link.set('aria-label', M.util.get_string('questiondependencyremove', 'quiz', a));
link.setData('action', 'removedependency');
window.require(['core/templates'], function(Templates) {
Templates.renderPix('t/locked', 'core', M.util.get_string('questiondependsonprevious', 'quiz')).then(
function(html) {
icon.replace(html);
}
);
});
iconname = 't/locked';
} else {
link.set('title', M.util.get_string('questiondependencyadd', 'quiz', a));
link.set('aria-label', M.util.get_string('questiondependencyadd', 'quiz', a));
link.setData('action', 'adddependency');
window.require(['core/templates'], function(Templates) {
Templates.renderPix('t/unlocked', 'core', M.util.get_string('questiondependencyfree', 'quiz')).then(
iconname = 't/unlocked';
}
window.require(['core/templates', 'core/notification'], function(Templates, Notification) {
Templates.renderPix(iconname, 'core', '')
.then(
function(html) {
icon.replace(html);
return;
}
);
});
}
).catch(Notification.exception);
});
}
};