From 9265ab5ca801bd429e1dbbc707e253c1d2acf28f Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Tue, 13 Aug 2024 11:25:29 +0800 Subject: [PATCH] MDL-82681 mod_assign: Add Suspended participants to Advanced filters Adds 'Suspended participants' filter to the 'Advanced' filters component on the assignment submissions page. --- .../grading/extra_filters_dropdown.min.js | 2 +- .../grading/extra_filters_dropdown.min.js.map | 2 +- .../grading/extra_filters_dropdown.js | 37 ++++++- .../classes/output/grading_actionmenu.php | 10 ++ mod/assign/lang/en/assign.php | 1 + mod/assign/locallib.php | 8 ++ .../extra_filters_dropdown_body.mustache | 12 +++ .../behat/filter_by_marking_workflow.feature | 4 +- .../filter_suspended_participants.feature | 98 +++++++++++++++++++ 9 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 mod/assign/tests/behat/filter_suspended_participants.feature diff --git a/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js b/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js index 75e9e43117f..58d4d99f7b1 100644 --- a/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js +++ b/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js @@ -5,6 +5,6 @@ define("mod_assign/actionbar/grading/extra_filters_dropdown",["exports","core/lo * @module mod_assign/actionbar/grading/extra_filters_dropdown * @copyright 2024 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const Selectors_extraFiltersDropdown=".dropdown.extrafilters",Selectors_extraFiltersClose='a[data-action="close"]',Selectors_workflowFilterElement='select[name="workflowfilter"]',Selectors_markerFilterElement='select[name="markingallocationfilter"]',restoreAppliedWorkflowFilter=async extraFiltersDropdown=>{const appliedWorkflowFilter=await(0,_repository.getUserPreference)("assign_workflowfilter");extraFiltersDropdown.getElement().querySelector(Selectors_workflowFilterElement).value=appliedWorkflowFilter},restoreAppliedMarkerFilter=async extraFiltersDropdown=>{const markerFilterSelect=extraFiltersDropdown.getElement().querySelector(Selectors_markerFilterElement);if(markerFilterSelect){const appliedMarkerFilter=await(0,_repository.getUserPreference)("assign_markerfilter");markerFilterSelect.value=appliedMarkerFilter}};_exports.init=()=>{const extraFiltersDropdown=(0,_dialog.getDropdownDialog)(Selectors_extraFiltersDropdown);extraFiltersDropdown&&(extraFiltersDropdown=>{extraFiltersDropdown.getElement().addEventListener("click",(e=>{e.target.closest(Selectors_extraFiltersClose)&&(e.preventDefault(),extraFiltersDropdown.setVisible(!1))})),(0,_jquery.default)(extraFiltersDropdown.getElement()).on("hide.bs.dropdown",(()=>{restoreAppliedWorkflowFilter(extraFiltersDropdown),restoreAppliedMarkerFilter(extraFiltersDropdown)}))})(extraFiltersDropdown)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const Selectors_extraFiltersDropdown=".dropdown.extrafilters",Selectors_extraFiltersClose='a[data-action="close"]',Selectors_workflowFilterElement='select[name="workflowfilter"]',Selectors_markerFilterElement='select[name="markingallocationfilter"]',Selectors_suspendedParticipantsFilterCheckbox='input[type="checkbox"][name="suspendedparticipantsfilter"]',Selectors_suspendedParticipantsFilterHidden='input[type="hidden"][name="suspendedparticipantsfilter"]',restoreAppliedWorkflowFilter=async extraFiltersDropdown=>{const appliedWorkflowFilter=await(0,_repository.getUserPreference)("assign_workflowfilter");extraFiltersDropdown.getElement().querySelector(Selectors_workflowFilterElement).value=appliedWorkflowFilter},restoreAppliedMarkerFilter=async extraFiltersDropdown=>{const markerFilterSelect=extraFiltersDropdown.getElement().querySelector(Selectors_markerFilterElement);if(markerFilterSelect){const appliedMarkerFilter=await(0,_repository.getUserPreference)("assign_markerfilter");markerFilterSelect.value=appliedMarkerFilter}},restoreAppliedSuspendedParticipantsFilter=async extraFiltersDropdown=>{const suspendedParticipantsFilterCheckbox=extraFiltersDropdown.getElement().querySelector(Selectors_suspendedParticipantsFilterCheckbox);if(suspendedParticipantsFilterCheckbox){const suspendedParticipantsFilterHidden=suspendedParticipantsFilterCheckbox.parentNode.querySelector(Selectors_suspendedParticipantsFilterHidden),showOnlyActiveParticipants=await(0,_repository.getUserPreference)("grade_report_showonlyactiveenrol");suspendedParticipantsFilterCheckbox.checked=!showOnlyActiveParticipants,suspendedParticipantsFilterHidden.disabled=!showOnlyActiveParticipants}};_exports.init=()=>{const extraFiltersDropdown=(0,_dialog.getDropdownDialog)(Selectors_extraFiltersDropdown);extraFiltersDropdown&&(extraFiltersDropdown=>{extraFiltersDropdown.getElement().addEventListener("click",(e=>{e.target.closest(Selectors_extraFiltersClose)&&(e.preventDefault(),extraFiltersDropdown.setVisible(!1))})),extraFiltersDropdown.getElement().addEventListener("change",(e=>{const suspendedParticipantsFilterCheckbox=e.target.closest(Selectors_suspendedParticipantsFilterCheckbox);suspendedParticipantsFilterCheckbox&&(suspendedParticipantsFilterCheckbox.parentNode.querySelector(Selectors_suspendedParticipantsFilterHidden).disabled=suspendedParticipantsFilterCheckbox.checked)})),(0,_jquery.default)(extraFiltersDropdown.getElement()).on("hide.bs.dropdown",(()=>{restoreAppliedWorkflowFilter(extraFiltersDropdown),restoreAppliedMarkerFilter(extraFiltersDropdown),restoreAppliedSuspendedParticipantsFilter(extraFiltersDropdown)}))})(extraFiltersDropdown)}})); //# sourceMappingURL=extra_filters_dropdown.min.js.map \ No newline at end of file diff --git a/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js.map b/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js.map index 70b5ffa61ba..a9dd34f8c61 100644 --- a/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js.map +++ b/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js.map @@ -1 +1 @@ -{"version":3,"file":"extra_filters_dropdown.min.js","sources":["../../../src/actionbar/grading/extra_filters_dropdown.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport {getDropdownDialog} from 'core/local/dropdown/dialog';\nimport {getUserPreference} from 'core_user/repository';\nimport $ from 'jquery';\n\n/**\n * Module for the extra filters dropdown on the submissions page.\n *\n * @module mod_assign/actionbar/grading/extra_filters_dropdown\n * @copyright 2024 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n extraFiltersDropdown: '.dropdown.extrafilters',\n extraFiltersClose: 'a[data-action=\"close\"]',\n workflowFilterElement: 'select[name=\"workflowfilter\"]',\n markerFilterElement: 'select[name=\"markingallocationfilter\"]'\n};\n\n/**\n * Register event listeners for the extra filters dropdown.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst registerEventListeners = (extraFiltersDropdown) => {\n // Click event listener to the extra filters dropdown element.\n extraFiltersDropdown.getElement().addEventListener('click', e => {\n // The target is the 'Close' button.\n if (e.target.closest(Selectors.extraFiltersClose)) {\n e.preventDefault();\n extraFiltersDropdown.setVisible(false);\n }\n });\n // Event listener triggered upon hiding of the dropdown.\n $(extraFiltersDropdown.getElement()).on('hide.bs.dropdown', () => {\n // Restore the filters to their stored preference values once the dropdown is closed.\n restoreAppliedWorkflowFilter(extraFiltersDropdown);\n restoreAppliedMarkerFilter(extraFiltersDropdown);\n });\n};\n\n/**\n * Restores the currently applied workflow filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedWorkflowFilter = async(extraFiltersDropdown) => {\n const appliedWorkflowFilter = await getUserPreference('assign_workflowfilter');\n const workflowFilterSelect = extraFiltersDropdown.getElement().querySelector(Selectors.workflowFilterElement);\n workflowFilterSelect.value = appliedWorkflowFilter;\n};\n\n/**\n * Restores the currently applied marker filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedMarkerFilter = async(extraFiltersDropdown) => {\n const markerFilterSelect = extraFiltersDropdown.getElement().querySelector(Selectors.markerFilterElement);\n if (markerFilterSelect) {\n const appliedMarkerFilter = await getUserPreference('assign_markerfilter');\n markerFilterSelect.value = appliedMarkerFilter;\n }\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const extraFiltersDropdown = getDropdownDialog(Selectors.extraFiltersDropdown);\n if (extraFiltersDropdown) {\n registerEventListeners(extraFiltersDropdown);\n }\n};\n"],"names":["Selectors","restoreAppliedWorkflowFilter","async","appliedWorkflowFilter","extraFiltersDropdown","getElement","querySelector","value","restoreAppliedMarkerFilter","markerFilterSelect","appliedMarkerFilter","addEventListener","e","target","closest","preventDefault","setVisible","on","registerEventListeners"],"mappings":";;;;;;;gJA4BMA,+BACoB,yBADpBA,4BAEiB,yBAFjBA,gCAGqB,gCAHrBA,8BAImB,yCA8BnBC,6BAA+BC,MAAAA,6BAC3BC,4BAA8B,iCAAkB,yBACzBC,qBAAqBC,aAAaC,cAAcN,iCACxDO,MAAQJ,uBAQ3BK,2BAA6BN,MAAAA,6BACzBO,mBAAqBL,qBAAqBC,aAAaC,cAAcN,kCACvES,mBAAoB,OACdC,0BAA4B,iCAAkB,uBACpDD,mBAAmBF,MAAQG,oCAOf,WACVN,sBAAuB,6BAAkBJ,gCAC3CI,sBA9CwBA,CAAAA,uBAE5BA,qBAAqBC,aAAaM,iBAAiB,SAASC,IAEpDA,EAAEC,OAAOC,QAAQd,+BACjBY,EAAEG,iBACFX,qBAAqBY,YAAW,2BAItCZ,qBAAqBC,cAAcY,GAAG,oBAAoB,KAExDhB,6BAA6BG,sBAC7BI,2BAA2BJ,0BAkC3Bc,CAAuBd"} \ No newline at end of file +{"version":3,"file":"extra_filters_dropdown.min.js","sources":["../../../src/actionbar/grading/extra_filters_dropdown.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport {getDropdownDialog} from 'core/local/dropdown/dialog';\nimport {getUserPreference} from 'core_user/repository';\nimport $ from 'jquery';\n\n/**\n * Module for the extra filters dropdown on the submissions page.\n *\n * @module mod_assign/actionbar/grading/extra_filters_dropdown\n * @copyright 2024 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n extraFiltersDropdown: '.dropdown.extrafilters',\n extraFiltersClose: 'a[data-action=\"close\"]',\n workflowFilterElement: 'select[name=\"workflowfilter\"]',\n markerFilterElement: 'select[name=\"markingallocationfilter\"]',\n suspendedParticipantsFilterCheckbox: 'input[type=\"checkbox\"][name=\"suspendedparticipantsfilter\"]',\n suspendedParticipantsFilterHidden: 'input[type=\"hidden\"][name=\"suspendedparticipantsfilter\"]'\n};\n\n/**\n * Register event listeners for the extra filters dropdown.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst registerEventListeners = (extraFiltersDropdown) => {\n // Click event listener to the extra filters dropdown element.\n extraFiltersDropdown.getElement().addEventListener('click', e => {\n // The target is the 'Close' button.\n if (e.target.closest(Selectors.extraFiltersClose)) {\n e.preventDefault();\n extraFiltersDropdown.setVisible(false);\n }\n });\n\n // Change event listener to the extra filters dropdown element.\n extraFiltersDropdown.getElement().addEventListener('change', e => {\n const suspendedParticipantsFilterCheckbox = e.target.closest(Selectors.suspendedParticipantsFilterCheckbox);\n // The target is the 'Suspended participants' filter checkbox.\n if (suspendedParticipantsFilterCheckbox) {\n // The 'Suspended participants' filter uses a hidden input and a checkbox. The hidden input is used to\n // submit '0' as a workaround when the checkbox is unchecked since unchecked checkboxes are not submitted\n // with the form. Therefore, we need to enable or disable the hidden input based on the checkbox state.\n const suspendedParticipantsFilterHidden = suspendedParticipantsFilterCheckbox.parentNode\n .querySelector(Selectors.suspendedParticipantsFilterHidden);\n suspendedParticipantsFilterHidden.disabled = suspendedParticipantsFilterCheckbox.checked;\n }\n });\n\n // Event listener triggered upon hiding of the dropdown.\n $(extraFiltersDropdown.getElement()).on('hide.bs.dropdown', () => {\n // Restore the filters to their stored preference values once the dropdown is closed.\n restoreAppliedWorkflowFilter(extraFiltersDropdown);\n restoreAppliedMarkerFilter(extraFiltersDropdown);\n restoreAppliedSuspendedParticipantsFilter(extraFiltersDropdown);\n });\n};\n\n/**\n * Restores the currently applied workflow filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedWorkflowFilter = async(extraFiltersDropdown) => {\n const appliedWorkflowFilter = await getUserPreference('assign_workflowfilter');\n const workflowFilterSelect = extraFiltersDropdown.getElement().querySelector(Selectors.workflowFilterElement);\n workflowFilterSelect.value = appliedWorkflowFilter;\n};\n\n/**\n * Restores the currently applied marker filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedMarkerFilter = async(extraFiltersDropdown) => {\n const markerFilterSelect = extraFiltersDropdown.getElement().querySelector(Selectors.markerFilterElement);\n if (markerFilterSelect) {\n const appliedMarkerFilter = await getUserPreference('assign_markerfilter');\n markerFilterSelect.value = appliedMarkerFilter;\n }\n};\n\n/**\n * Restores the currently suspended participants filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedSuspendedParticipantsFilter = async(extraFiltersDropdown) => {\n const suspendedParticipantsFilterCheckbox = extraFiltersDropdown.getElement()\n .querySelector(Selectors.suspendedParticipantsFilterCheckbox);\n if (suspendedParticipantsFilterCheckbox) {\n const suspendedParticipantsFilterHidden = suspendedParticipantsFilterCheckbox.parentNode\n .querySelector(Selectors.suspendedParticipantsFilterHidden);\n const showOnlyActiveParticipants = await getUserPreference('grade_report_showonlyactiveenrol');\n suspendedParticipantsFilterCheckbox.checked = !showOnlyActiveParticipants;\n suspendedParticipantsFilterHidden.disabled = !showOnlyActiveParticipants;\n }\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const extraFiltersDropdown = getDropdownDialog(Selectors.extraFiltersDropdown);\n if (extraFiltersDropdown) {\n registerEventListeners(extraFiltersDropdown);\n }\n};\n"],"names":["Selectors","restoreAppliedWorkflowFilter","async","appliedWorkflowFilter","extraFiltersDropdown","getElement","querySelector","value","restoreAppliedMarkerFilter","markerFilterSelect","appliedMarkerFilter","restoreAppliedSuspendedParticipantsFilter","suspendedParticipantsFilterCheckbox","suspendedParticipantsFilterHidden","parentNode","showOnlyActiveParticipants","checked","disabled","addEventListener","e","target","closest","preventDefault","setVisible","on","registerEventListeners"],"mappings":";;;;;;;gJA4BMA,+BACoB,yBADpBA,4BAEiB,yBAFjBA,gCAGqB,gCAHrBA,8BAImB,yCAJnBA,8CAKmC,6DALnCA,4CAMiC,2DA8CjCC,6BAA+BC,MAAAA,6BAC3BC,4BAA8B,iCAAkB,yBACzBC,qBAAqBC,aAAaC,cAAcN,iCACxDO,MAAQJ,uBAQ3BK,2BAA6BN,MAAAA,6BACzBO,mBAAqBL,qBAAqBC,aAAaC,cAAcN,kCACvES,mBAAoB,OACdC,0BAA4B,iCAAkB,uBACpDD,mBAAmBF,MAAQG,sBAS7BC,0CAA4CT,MAAAA,6BACxCU,oCAAsCR,qBAAqBC,aAC5DC,cAAcN,kDACfY,oCAAqC,OAC/BC,kCAAoCD,oCAAoCE,WACzER,cAAcN,6CACbe,iCAAmC,iCAAkB,oCAC3DH,oCAAoCI,SAAWD,2BAC/CF,kCAAkCI,UAAYF,2CAOlC,WACVX,sBAAuB,6BAAkBJ,gCAC3CI,sBA/EwBA,CAAAA,uBAE5BA,qBAAqBC,aAAaa,iBAAiB,SAASC,IAEpDA,EAAEC,OAAOC,QAAQrB,+BACjBmB,EAAEG,iBACFlB,qBAAqBmB,YAAW,OAKxCnB,qBAAqBC,aAAaa,iBAAiB,UAAUC,UACnDP,oCAAsCO,EAAEC,OAAOC,QAAQrB,+CAEzDY,sCAI0CA,oCAAoCE,WACzER,cAAcN,6CACeiB,SAAWL,oCAAoCI,gCAKvFZ,qBAAqBC,cAAcmB,GAAG,oBAAoB,KAExDvB,6BAA6BG,sBAC7BI,2BAA2BJ,sBAC3BO,0CAA0CP,0BAmD1CqB,CAAuBrB"} \ No newline at end of file diff --git a/mod/assign/amd/src/actionbar/grading/extra_filters_dropdown.js b/mod/assign/amd/src/actionbar/grading/extra_filters_dropdown.js index 1e9fa2c3f0c..3509d16ed52 100644 --- a/mod/assign/amd/src/actionbar/grading/extra_filters_dropdown.js +++ b/mod/assign/amd/src/actionbar/grading/extra_filters_dropdown.js @@ -30,7 +30,9 @@ const Selectors = { extraFiltersDropdown: '.dropdown.extrafilters', extraFiltersClose: 'a[data-action="close"]', workflowFilterElement: 'select[name="workflowfilter"]', - markerFilterElement: 'select[name="markingallocationfilter"]' + markerFilterElement: 'select[name="markingallocationfilter"]', + suspendedParticipantsFilterCheckbox: 'input[type="checkbox"][name="suspendedparticipantsfilter"]', + suspendedParticipantsFilterHidden: 'input[type="hidden"][name="suspendedparticipantsfilter"]' }; /** @@ -47,11 +49,27 @@ const registerEventListeners = (extraFiltersDropdown) => { extraFiltersDropdown.setVisible(false); } }); + + // Change event listener to the extra filters dropdown element. + extraFiltersDropdown.getElement().addEventListener('change', e => { + const suspendedParticipantsFilterCheckbox = e.target.closest(Selectors.suspendedParticipantsFilterCheckbox); + // The target is the 'Suspended participants' filter checkbox. + if (suspendedParticipantsFilterCheckbox) { + // The 'Suspended participants' filter uses a hidden input and a checkbox. The hidden input is used to + // submit '0' as a workaround when the checkbox is unchecked since unchecked checkboxes are not submitted + // with the form. Therefore, we need to enable or disable the hidden input based on the checkbox state. + const suspendedParticipantsFilterHidden = suspendedParticipantsFilterCheckbox.parentNode + .querySelector(Selectors.suspendedParticipantsFilterHidden); + suspendedParticipantsFilterHidden.disabled = suspendedParticipantsFilterCheckbox.checked; + } + }); + // Event listener triggered upon hiding of the dropdown. $(extraFiltersDropdown.getElement()).on('hide.bs.dropdown', () => { // Restore the filters to their stored preference values once the dropdown is closed. restoreAppliedWorkflowFilter(extraFiltersDropdown); restoreAppliedMarkerFilter(extraFiltersDropdown); + restoreAppliedSuspendedParticipantsFilter(extraFiltersDropdown); }); }; @@ -79,6 +97,23 @@ const restoreAppliedMarkerFilter = async(extraFiltersDropdown) => { } }; +/** + * Restores the currently suspended participants filter to its stored preference value. + * + * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance. + */ +const restoreAppliedSuspendedParticipantsFilter = async(extraFiltersDropdown) => { + const suspendedParticipantsFilterCheckbox = extraFiltersDropdown.getElement() + .querySelector(Selectors.suspendedParticipantsFilterCheckbox); + if (suspendedParticipantsFilterCheckbox) { + const suspendedParticipantsFilterHidden = suspendedParticipantsFilterCheckbox.parentNode + .querySelector(Selectors.suspendedParticipantsFilterHidden); + const showOnlyActiveParticipants = await getUserPreference('grade_report_showonlyactiveenrol'); + suspendedParticipantsFilterCheckbox.checked = !showOnlyActiveParticipants; + suspendedParticipantsFilterHidden.disabled = !showOnlyActiveParticipants; + } +}; + /** * Initialize module. */ diff --git a/mod/assign/classes/output/grading_actionmenu.php b/mod/assign/classes/output/grading_actionmenu.php index 7613db27757..b02af750ab9 100644 --- a/mod/assign/classes/output/grading_actionmenu.php +++ b/mod/assign/classes/output/grading_actionmenu.php @@ -318,6 +318,12 @@ class grading_actionmenu implements templatable, renderable { } } + if (has_capability('moodle/course:viewsuspendedusers', $this->assign->get_context())) { + $dropdowncontentdata['filters']['suspendedparticipants'] = [ + 'active' => !$this->assign->show_only_active_users(), + ]; + } + // If there are no available filters, return null. if (empty($dropdowncontentdata['filters'])) { return null; @@ -365,7 +371,11 @@ class grading_actionmenu implements templatable, renderable { if ($canallocatemarker && get_user_preferences('assign_markerfilter')) { $appliedextrafilterscount++; } + } + // If suspended participants are included. + if (!$this->assign->show_only_active_users()) { + $appliedextrafilterscount++; } return $appliedextrafilterscount; diff --git a/mod/assign/lang/en/assign.php b/mod/assign/lang/en/assign.php index c0a84efc89c..9cf48536afa 100644 --- a/mod/assign/lang/en/assign.php +++ b/mod/assign/lang/en/assign.php @@ -344,6 +344,7 @@ Note that this setting has no effect on the comments box on the grading page.'; $string['hideshow'] = 'Hide/Show'; $string['hiddenuser'] = 'Participant '; $string['inactiveoverridehelp'] = '* This override is inactive because the user\'s access to the activity is restricted. This can be due to group or role assignments, other access restrictions, or the activity being hidden.'; +$string['includesuspendedparticipants'] = 'Include suspended participants'; $string['indicator:cognitivedepth'] = 'Assignment cognitive'; $string['indicator:cognitivedepth_help'] = 'This indicator is based on the cognitive depth reached by the student in an assignment activity.'; $string['indicator:cognitivedepthdef'] = 'Assignment cognitive'; diff --git a/mod/assign/locallib.php b/mod/assign/locallib.php index d3ece3fcd94..ca00b74d9a6 100644 --- a/mod/assign/locallib.php +++ b/mod/assign/locallib.php @@ -4534,6 +4534,14 @@ class assign { set_user_preference('assign_markerfilter', $markingallocationfilter); } + // Retrieve the 'suspendedparticipantsfilter' parameter, or set it to null if not provided. + $suspendedparticipantsfilter = optional_param('suspendedparticipantsfilter', null, PARAM_BOOL); + if ($suspendedparticipantsfilter !== null && + has_capability('moodle/course:viewsuspendedusers', $this->get_context())) { + // Save the 'suspendedparticipantsfilter' value as a user preference. + set_user_preference('grade_report_showonlyactiveenrol', !$suspendedparticipantsfilter); + } + $controller = $gradingmanager->get_active_controller(); $showquickgrading = empty($controller) && $this->can_grade(); $quickgrading = get_user_preferences('assign_quickgrading', false); diff --git a/mod/assign/templates/actionbar/grading/extra_filters_dropdown_body.mustache b/mod/assign/templates/actionbar/grading/extra_filters_dropdown_body.mustache index a53b22a39e6..a3e4f46e023 100644 --- a/mod/assign/templates/actionbar/grading/extra_filters_dropdown_body.mustache +++ b/mod/assign/templates/actionbar/grading/extra_filters_dropdown_body.mustache @@ -30,6 +30,8 @@ * key - The key of the marking allocation filter option * name - The name of the marking allocation filter option * active - Whether the marking allocation filter option is currently active + * suspendedparticipants + * active - Whether the suspended participants filter is currently active Example context (json): { @@ -55,6 +57,9 @@ "active": true } ] + }, + "suspendedparticipants": { + "active": true } } ] @@ -89,6 +94,13 @@ {{/markingallocation}} + {{#suspendedparticipants}} +
+ + + +
+ {{/suspendedparticipants}} {{/filters}}
{{#str}}closebuttontitle{{/str}} diff --git a/mod/assign/tests/behat/filter_by_marking_workflow.feature b/mod/assign/tests/behat/filter_by_marking_workflow.feature index ef909fb90cb..ead8c521cd4 100644 --- a/mod/assign/tests/behat/filter_by_marking_workflow.feature +++ b/mod/assign/tests/behat/filter_by_marking_workflow.feature @@ -30,15 +30,13 @@ Feature: In an assignment, teachers can filter displayed submissions by marking Given I am on the "Test assignment" Activity page logged in as teacher1 And I navigate to "Submissions" in current page administration And I change window size to "large" - And "Advanced" "button" should not exist in the ".tertiary-navigation" "css_element" When I am on the "Test assignment" "assign activity editing" page And I expand all fieldsets And I set the field "Use marking workflow" to "Yes" And I press "Save and display" And I navigate to "Submissions" in current page administration - Then "Advanced" "button" should exist in the ".tertiary-navigation" "css_element" And I click on "Advanced" "button" in the ".tertiary-navigation" "css_element" - And "Marking state" "select" should exist in the ".extrafilters .dropdown-menu" "css_element" + Then "Marking state" "select" should exist in the ".extrafilters .dropdown-menu" "css_element" And the field "Marking state" matches value "No filter" And the "Marking state" select box should contain "No filter" And the "Marking state" select box should contain "Not marked" diff --git a/mod/assign/tests/behat/filter_suspended_participants.feature b/mod/assign/tests/behat/filter_suspended_participants.feature new file mode 100644 index 00000000000..6ae2128d307 --- /dev/null +++ b/mod/assign/tests/behat/filter_suspended_participants.feature @@ -0,0 +1,98 @@ +@mod @mod_assign +Feature: In an assignment, teachers can include or exclude submissions from suspended participants + In order to manage submissions more easily + As a teacher + I need to be able to include or exclude submissions from suspended participants. + + Background: + Given the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student1@example.com | + | student2 | Student | 2 | student2@example.com | + And the following "course enrolments" exist: + | user | course | role | status | + | teacher1 | C1 | editingteacher | 0 | + | student1 | C1 | student | 0 | + | student2 | C1 | student | 1 | + And the following "activity" exists: + | activity | assign | + | course | C1 | + | name | Test assignment name | + + @javascript + Scenario: The suspended participants filter is available only when the teacher has the capability to view suspended participants + Given the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | moodle/course:viewsuspendedusers | Prevent | editingteacher | Course | C1 | + And I am on the "Test assignment name" Activity page logged in as teacher1 + And I navigate to "Submissions" in current page administration + When I change window size to "large" + # Ensure the Advanced filters component is not available. This validates that the Suspended participants filer is + # not available as well since it's part of this component. + Then "Advanced" "button" should not exist in the ".tertiary-navigation" "css_element" + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | moodle/course:viewsuspendedusers | Allow | editingteacher | Course | C1 | + And I reload the page + And "Advanced" "button" should exist in the ".tertiary-navigation" "css_element" + And I click on "Advanced" "button" in the ".tertiary-navigation" "css_element" + And "Include suspended participants" "checkbox" should exist in the ".extrafilters .dropdown-menu" "css_element" + And the field "Include suspended participants" matches value "" + + @javascript + Scenario: Teacher can include or exclude submissions from suspended participants + Given I am on the "Test assignment name" Activity page logged in as teacher1 + And I navigate to "Submissions" in current page administration + And I change window size to "large" + And the following should exist in the "submissions" table: + | -2- | + | Student 1 | + And the following should not exist in the "submissions" table: + | -2- | + | Student 2 | + # Set to include submissions from suspended participants. + And I click on "Advanced" "button" in the ".tertiary-navigation" "css_element" + When I click on "Include suspended participants" "checkbox" in the ".extrafilters .dropdown-menu" "css_element" + And I click on "Apply" "button" in the ".extrafilters .dropdown-menu" "css_element" + # Ensure that the suspended Student 2 is now also displayed in the submissions table. + Then the following should exist in the "submissions" table: + | -2- | + | Student 1 | + | Student 2 | + # Ensure the badge indicating the number of applied filters is present. + And ".badge" "css_element" should exist in the ".extrafilters .dropdown-toggle" "css_element" + And I should see "+1" in the ".extrafilters .badge" "css_element" + # Ensure the filter remains applied when navigating away from and returning to the assignment submissions page. + And I am on the "Test assignment name" Activity page + And I navigate to "Submissions" in current page administration + And the following should exist in the "submissions" table: + | -2- | + | Student 1 | + | Student 2 | + And I click on "Advanced" "button" in the ".tertiary-navigation" "css_element" + And the field "Include suspended participants" matches value "1" + # Ensure the filter is not applied unless the 'Apply' button is pressed. + And I click on "Include suspended participants" "checkbox" in the ".extrafilters .dropdown-menu" "css_element" + And the field "Include suspended participants" matches value "" + And I click on "Close" "link" in the ".extrafilters .dropdown-menu" "css_element" + And the following should exist in the "submissions" table: + | -2- | + | Student 1 | + | Student 2 | + And I click on "Advanced" "button" in the ".tertiary-navigation" "css_element" + And the field "Include suspended participants" matches value "1" + # Set to exclude submissions from suspended participants. + And I click on "Include suspended participants" "checkbox" in the ".extrafilters .dropdown-menu" "css_element" + And the field "Include suspended participants" matches value "" + And I click on "Apply" "button" in the ".extrafilters .dropdown-menu" "css_element" + # Ensure only Student 1 is now displayed in the submissions table. + And the following should exist in the "submissions" table: + | -2- | + | Student 1 | + And the following should not exist in the "submissions" table: + | -2- | + | Student 2 |