diff --git a/grade/lib.php b/grade/lib.php index c6692160e6b..4c7e3f90c22 100644 --- a/grade/lib.php +++ b/grade/lib.php @@ -2215,7 +2215,7 @@ class grade_structure { */ public function get_locking_link(array $element, object $gpr, array $langstrings): ?string { - if (has_capability('moodle/grade:manage', $this->context)) { + if (has_capability('moodle/grade:manage', $this->context) && isset($element['object'])) { $title = ''; $url = new moodle_url('/grade/edit/tree/action.php', ['id' => $this->courseid, 'sesskey' => sesskey(), 'eid' => $element['eid']]); @@ -2311,7 +2311,7 @@ class grade_structure { public function get_edit_calculation_link(array $element, object $gpr, string $editcalculationstrings): ?string { - if (has_capability('moodle/grade:manage', $this->context)) { + if (has_capability('moodle/grade:manage', $this->context) && isset($element['object'])) { $object = $element['object']; $isscale = $object->gradetype == GRADE_TYPE_SCALE; $isvalue = $object->gradetype == GRADE_TYPE_VALUE; @@ -2345,6 +2345,24 @@ class grade_structure { return html_writer::link($urlnew, $title, ['class' => 'dropdown-item', 'aria-label' => $title, 'aria-current' => $active, 'role' => 'menuitem']); } + + /** + * Returns link to sort grade item column + * + * @param moodle_url $sortlink A base link for sorting + * @param object $gpr A grade_plugin_return object + * @param string $title Language string + * @param string $direction Direction od sorting + * @return string + */ + public function get_sorting_link(moodle_url $sortlink, object $gpr, string $title, string $direction = 'asc'): string { + + $sortlink->param('sort', $direction); + $gpr->add_url_params($sortlink); + return html_writer::link($sortlink, $title, + ['class' => 'dropdown-item', 'aria-label' => $title, 'role' => 'menuitem']); + } + } /** diff --git a/grade/report/grader/amd/build/stickycolspan.min.js b/grade/report/grader/amd/build/stickycolspan.min.js index fc6bd4d8388..33ea4088e4d 100644 --- a/grade/report/grader/amd/build/stickycolspan.min.js +++ b/grade/report/grader/amd/build/stickycolspan.min.js @@ -6,6 +6,6 @@ define("gradereport_grader/stickycolspan",["exports"],(function(_exports){Object * @copyright 2022 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -const SELECTORS_GRADEPARENT=".gradeparent",SELECTORS_STUDENTHEADER="#studentheader",SELECTORS_TABLEHEADER="th.header",SELECTORS_BEHAT="body.behat-site";_exports.init=()=>{if(document.querySelector(SELECTORS_BEHAT))return;const grader=document.querySelector(SELECTORS_GRADEPARENT),studentHeader=grader.querySelector(SELECTORS_STUDENTHEADER),leftOffset=getComputedStyle(studentHeader).getPropertyValue("left"),rightOffset=getComputedStyle(studentHeader).getPropertyValue("right"),tableHeaders=grader.querySelectorAll(SELECTORS_TABLEHEADER);for(let i=0;i1){const addOffset=tableHeaders[i].offsetWidth-studentHeader.offsetWidth;window.right_to_left()?tableHeaders[i].style.right="calc("+rightOffset+" - "+addOffset+"px )":tableHeaders[i].style.left="calc("+leftOffset+" - "+addOffset+"px )"}else tableHeaders[i].style.zIndex=tableHeaders.length-i}})); +const SELECTORS_GRADEPARENT=".gradeparent",SELECTORS_STUDENTHEADER="#studentheader",SELECTORS_TABLEHEADER="th.header",SELECTORS_BEHAT="body.behat-site",SELECTORS_TABLEHEADING="tr.heading";_exports.init=()=>{if(document.querySelector(SELECTORS_BEHAT))return;const grader=document.querySelector(SELECTORS_GRADEPARENT),studentHeader=grader.querySelector(SELECTORS_STUDENTHEADER),leftOffset=getComputedStyle(studentHeader).getPropertyValue("left"),rightOffset=getComputedStyle(studentHeader).getPropertyValue("right"),tableHeaders=grader.querySelectorAll(SELECTORS_TABLEHEADER);let i=0;tableHeaders.forEach((tableHeader=>{if(tableHeader.colSpan>1){const addOffset=tableHeader.offsetWidth-studentHeader.offsetWidth;window.right_to_left()?tableHeader.style.right="calc("+rightOffset+" - "+addOffset+"px )":tableHeader.style.left="calc("+leftOffset+" - "+addOffset+"px )"}else tableHeader.style.zIndex=tableHeaders.length-i;i++})),grader.querySelector(SELECTORS_TABLEHEADING).style.zIndex=tableHeaders.length+1}})); //# sourceMappingURL=stickycolspan.min.js.map \ No newline at end of file diff --git a/grade/report/grader/amd/build/stickycolspan.min.js.map b/grade/report/grader/amd/build/stickycolspan.min.js.map index 84745ecc357..969395aba33 100644 --- a/grade/report/grader/amd/build/stickycolspan.min.js.map +++ b/grade/report/grader/amd/build/stickycolspan.min.js.map @@ -1 +1 @@ -{"version":3,"file":"stickycolspan.min.js","sources":["../src/stickycolspan.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\n/**\n * Javascript module for fixing the position of sticky headers with multiple colspans\n *\n * @module gradereport_grader/stickycolspan\n * @copyright 2022 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst SELECTORS = {\n GRADEPARENT: '.gradeparent',\n STUDENTHEADER: '#studentheader',\n TABLEHEADER: 'th.header',\n BEHAT: 'body.behat-site',\n AVERAGEROW: 'tr.lastrow',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n if (document.querySelector(SELECTORS.BEHAT)) {\n return;\n }\n const grader = document.querySelector(SELECTORS.GRADEPARENT);\n const studentHeader = grader.querySelector(SELECTORS.STUDENTHEADER);\n const leftOffset = getComputedStyle(studentHeader).getPropertyValue('left');\n const rightOffset = getComputedStyle(studentHeader).getPropertyValue('right');\n\n const tableHeaders = grader.querySelectorAll(SELECTORS.TABLEHEADER);\n\n for (let i = 0; i < tableHeaders.length; i++) {\n if (tableHeaders[i].colSpan > 1) {\n const addOffset = (tableHeaders[i].offsetWidth - studentHeader.offsetWidth);\n if (window.right_to_left()) {\n tableHeaders[i].style.right = 'calc(' + rightOffset + ' - ' + addOffset + 'px )';\n } else {\n tableHeaders[i].style.left = 'calc(' + leftOffset + ' - ' + addOffset + 'px )';\n }\n } else {\n tableHeaders[i].style.zIndex = tableHeaders.length - i;\n }\n }\n\n};\n"],"names":["SELECTORS","document","querySelector","grader","studentHeader","leftOffset","getComputedStyle","getPropertyValue","rightOffset","tableHeaders","querySelectorAll","i","length","colSpan","addOffset","offsetWidth","window","right_to_left","style","right","left","zIndex"],"mappings":";;;;;;;;MAuBMA,sBACW,eADXA,wBAEa,iBAFbA,sBAGW,YAHXA,gBAIK,gCAOS,QACZC,SAASC,cAAcF,8BAGrBG,OAASF,SAASC,cAAcF,uBAChCI,cAAgBD,OAAOD,cAAcF,yBACrCK,WAAaC,iBAAiBF,eAAeG,iBAAiB,QAC9DC,YAAcF,iBAAiBF,eAAeG,iBAAiB,SAE/DE,aAAeN,OAAOO,iBAAiBV,2BAExC,IAAIW,EAAI,EAAGA,EAAIF,aAAaG,OAAQD,OACjCF,aAAaE,GAAGE,QAAU,EAAG,OACvBC,UAAaL,aAAaE,GAAGI,YAAcX,cAAcW,YAC3DC,OAAOC,gBACPR,aAAaE,GAAGO,MAAMC,MAAQ,QAAUX,YAAc,MAAQM,UAAY,OAE1EL,aAAaE,GAAGO,MAAME,KAAO,QAAUf,WAAa,MAAQS,UAAY,YAG5EL,aAAaE,GAAGO,MAAMG,OAASZ,aAAaG,OAASD"} \ No newline at end of file +{"version":3,"file":"stickycolspan.min.js","sources":["../src/stickycolspan.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\n/**\n * Javascript module for fixing the position of sticky headers with multiple colspans\n *\n * @module gradereport_grader/stickycolspan\n * @copyright 2022 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst SELECTORS = {\n GRADEPARENT: '.gradeparent',\n STUDENTHEADER: '#studentheader',\n TABLEHEADER: 'th.header',\n BEHAT: 'body.behat-site',\n AVERAGEROW: 'tr.lastrow',\n TABLEHEADING: 'tr.heading',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n if (document.querySelector(SELECTORS.BEHAT)) {\n return;\n }\n const grader = document.querySelector(SELECTORS.GRADEPARENT);\n const studentHeader = grader.querySelector(SELECTORS.STUDENTHEADER);\n const leftOffset = getComputedStyle(studentHeader).getPropertyValue('left');\n const rightOffset = getComputedStyle(studentHeader).getPropertyValue('right');\n\n const tableHeaders = grader.querySelectorAll(SELECTORS.TABLEHEADER);\n let i = 0;\n tableHeaders.forEach((tableHeader) => {\n if (tableHeader.colSpan > 1) {\n const addOffset = (tableHeader.offsetWidth - studentHeader.offsetWidth);\n if (window.right_to_left()) {\n tableHeader.style.right = 'calc(' + rightOffset + ' - ' + addOffset + 'px )';\n } else {\n tableHeader.style.left = 'calc(' + leftOffset + ' - ' + addOffset + 'px )';\n }\n } else {\n tableHeader.style.zIndex = tableHeaders.length - i;\n }\n i++;\n });\n\n let tableHeader = grader.querySelector(SELECTORS.TABLEHEADING);\n tableHeader.style.zIndex = tableHeaders.length + 1;\n\n\n};\n"],"names":["SELECTORS","document","querySelector","grader","studentHeader","leftOffset","getComputedStyle","getPropertyValue","rightOffset","tableHeaders","querySelectorAll","i","forEach","tableHeader","colSpan","addOffset","offsetWidth","window","right_to_left","style","right","left","zIndex","length"],"mappings":";;;;;;;;MAuBMA,sBACW,eADXA,wBAEa,iBAFbA,sBAGW,YAHXA,gBAIK,kBAJLA,uBAMY,2BAME,QACZC,SAASC,cAAcF,8BAGrBG,OAASF,SAASC,cAAcF,uBAChCI,cAAgBD,OAAOD,cAAcF,yBACrCK,WAAaC,iBAAiBF,eAAeG,iBAAiB,QAC9DC,YAAcF,iBAAiBF,eAAeG,iBAAiB,SAE/DE,aAAeN,OAAOO,iBAAiBV,2BACzCW,EAAI,EACRF,aAAaG,SAASC,iBACdA,YAAYC,QAAU,EAAG,OACnBC,UAAaF,YAAYG,YAAcZ,cAAcY,YACvDC,OAAOC,gBACPL,YAAYM,MAAMC,MAAQ,QAAUZ,YAAc,MAAQO,UAAY,OAEtEF,YAAYM,MAAME,KAAO,QAAUhB,WAAa,MAAQU,UAAY,YAGxEF,YAAYM,MAAMG,OAASb,aAAac,OAASZ,EAErDA,OAGcR,OAAOD,cAAcF,wBAC3BmB,MAAMG,OAASb,aAAac,OAAS"} \ No newline at end of file diff --git a/grade/report/grader/amd/src/stickycolspan.js b/grade/report/grader/amd/src/stickycolspan.js index 59b0b99f6d6..0cbdce47ea6 100644 --- a/grade/report/grader/amd/src/stickycolspan.js +++ b/grade/report/grader/amd/src/stickycolspan.js @@ -27,6 +27,7 @@ const SELECTORS = { TABLEHEADER: 'th.header', BEHAT: 'body.behat-site', AVERAGEROW: 'tr.lastrow', + TABLEHEADING: 'tr.heading', }; /** @@ -42,18 +43,23 @@ export const init = () => { const rightOffset = getComputedStyle(studentHeader).getPropertyValue('right'); const tableHeaders = grader.querySelectorAll(SELECTORS.TABLEHEADER); - - for (let i = 0; i < tableHeaders.length; i++) { - if (tableHeaders[i].colSpan > 1) { - const addOffset = (tableHeaders[i].offsetWidth - studentHeader.offsetWidth); + let i = 0; + tableHeaders.forEach((tableHeader) => { + if (tableHeader.colSpan > 1) { + const addOffset = (tableHeader.offsetWidth - studentHeader.offsetWidth); if (window.right_to_left()) { - tableHeaders[i].style.right = 'calc(' + rightOffset + ' - ' + addOffset + 'px )'; + tableHeader.style.right = 'calc(' + rightOffset + ' - ' + addOffset + 'px )'; } else { - tableHeaders[i].style.left = 'calc(' + leftOffset + ' - ' + addOffset + 'px )'; + tableHeader.style.left = 'calc(' + leftOffset + ' - ' + addOffset + 'px )'; } } else { - tableHeaders[i].style.zIndex = tableHeaders.length - i; + tableHeader.style.zIndex = tableHeaders.length - i; } - } + i++; + }); + + let tableHeader = grader.querySelector(SELECTORS.TABLEHEADING); + tableHeader.style.zIndex = tableHeaders.length + 1; + }; diff --git a/grade/report/grader/index.php b/grade/report/grader/index.php index 2d9058d8d4f..f0e93e45f5f 100644 --- a/grade/report/grader/index.php +++ b/grade/report/grader/index.php @@ -33,6 +33,7 @@ $page = optional_param('page', 0, PARAM_INT); // active page $edit = optional_param('edit', -1, PARAM_BOOL); // sticky editting mode $sortitemid = optional_param('sortitemid', 0, PARAM_ALPHANUMEXT); +$sort = optional_param('sort', '', PARAM_TEXT); $action = optional_param('action', 0, PARAM_ALPHAEXT); $move = optional_param('move', 0, PARAM_INT); $type = optional_param('type', 0, PARAM_ALPHA); @@ -114,7 +115,10 @@ grade_regrade_final_grades_if_required($course); //Initialise the grader report object that produces the table //the class grade_report_grader_ajax was removed as part of MDL-21562 -$report = new grade_report_grader($courseid, $gpr, $context, $page, $sortitemid); +if ($sort) { + $sort = strtoupper($sort); +} +$report = new grade_report_grader($courseid, $gpr, $context, $page, $sortitemid, $sort); $numusers = $report->get_numusers(true, true); $actionbar = new \gradereport_grader\output\action_bar($context, $report, $numusers); diff --git a/grade/report/grader/lib.php b/grade/report/grader/lib.php index 1fe890c88b6..573d3a59408 100644 --- a/grade/report/grader/lib.php +++ b/grade/report/grader/lib.php @@ -115,8 +115,9 @@ class grade_report_grader extends grade_report { * @param string $context * @param int $page The current page being viewed (when report is paged) * @param int $sortitemid The id of the grade_item by which to sort the table + * @param string $sort Sorting direction */ - public function __construct($courseid, $gpr, $context, $page=null, $sortitemid=null) { + public function __construct($courseid, $gpr, $context, $page=null, $sortitemid=null, string $sort = '') { global $CFG; parent::__construct($courseid, $gpr, $context, $page); @@ -155,7 +156,7 @@ class grade_report_grader extends grade_report { $this->setup_groups(); $this->setup_users(); - $this->setup_sortitemid(); + $this->setup_sortitemid($sort); $this->overridecat = (bool)get_config('moodle', 'grade_overridecat'); } @@ -324,8 +325,10 @@ class grade_report_grader extends grade_report { * Setting the sort order, this depends on last state * all this should be in the new table class that we might need to use * for displaying grades. + + * @param string $sort sorting direction */ - private function setup_sortitemid() { + private function setup_sortitemid(string $sort = '') { global $SESSION; @@ -336,7 +339,7 @@ class grade_report_grader extends grade_report { if ($this->sortitemid) { if (!isset($SESSION->gradeuserreport->sort)) { $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC'; - } else { + } else if (!$sort) { // this is the first sort, i.e. by last name if (!isset($SESSION->gradeuserreport->sortitemid)) { $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC'; @@ -367,6 +370,12 @@ class grade_report_grader extends grade_report { $this->sortorder = 'ASC'; } } + + // If explicit sorting direction exists. + if ($sort) { + $this->sortorder = $sort; + $SESSION->gradeuserreport->sort = $sort; + } } /** @@ -632,7 +641,9 @@ class grade_report_grader extends grade_report { $studentheader->scope = 'col'; $studentheader->header = true; $studentheader->id = 'studentheader'; - $studentheader->text = $arrows['studentname']; + $element = ['type' => 'userfield', 'name' => 'fullname']; + $studentheader->text = $arrows['studentname'] . $this->get_cell_action_menu($element, 'gradeitem'); + $headerrow->cells[] = $studentheader; foreach ($extrafields as $field) { @@ -640,7 +651,8 @@ class grade_report_grader extends grade_report { $fieldheader->attributes['class'] = 'userfield user' . $field; $fieldheader->scope = 'col'; $fieldheader->header = true; - $fieldheader->text = $arrows[$field]; + $element = ['type' => 'userfield', 'name' => $field]; + $fieldheader->text = $arrows[$field] . $this->get_cell_action_menu($element, 'gradeitem'); $headerrow->cells[] = $fieldheader; } @@ -724,7 +736,6 @@ class grade_report_grader extends grade_report { $numusers = count($this->users); $gradetabindex = 1; $strgrade = $this->get_lang_string('gradenoun'); - $this->get_sort_arrows(); // Get preferences once. $quickgrading = $this->get_pref('quickgrading'); @@ -796,9 +807,9 @@ class grade_report_grader extends grade_report { $arrow = ''; if ($element['object']->id == $this->sortitemid) { if ($this->sortorder == 'ASC') { - $arrow = $this->get_sort_arrow('up', $sortlink); - } else { $arrow = $this->get_sort_arrow('down', $sortlink); + } else { + $arrow = $this->get_sort_arrow('up', $sortlink); } } @@ -1632,6 +1643,9 @@ class grade_report_grader extends grade_report { $gradeanalysisstring = $this->get_lang_string('gradeanalysis', 'grades'); + $titleasc = $this->get_lang_string('asc'); + $titledesc = $this->get_lang_string('desc'); + if ($element['type'] == 'grade') { $item = $element['object']->grade_item; if ($item->is_course_item() || $item->is_category_item()) { @@ -1649,7 +1663,8 @@ class grade_report_grader extends grade_report { $context->gradeanalysisurl = $this->gtree->get_grade_analysis_link($element['object'], $gradeanalysisstring); } else if (($element['type'] == 'item') || ($element['type'] == 'categoryitem') || - ($element['type'] == 'courseitem')) { + ($element['type'] == 'courseitem') || + ($element['type'] == 'userfield')) { if ($element['type'] == 'item') { foreach ($this->get_report_links($this->context, $this->courseid, $element, $this->gpr, $mode) @@ -1660,9 +1675,14 @@ class grade_report_grader extends grade_report { $context->advancedgradingurl = $this->gtree->get_advanced_grading_link($element, $this->gpr); } + if ($element['type'] == 'item') { + $context->divider1 = true; + } if (!empty($USER->editing)) { - $context->divider = true; - + if ($element['type'] !== 'userfield') { + $context->divider1 = true; + $context->divider2 = true; + } if ($element['type'] == 'item') { $context->editurl = $this->gtree->get_edit_link($element, $this->gpr, $editstrings); } @@ -1670,12 +1690,36 @@ class grade_report_grader extends grade_report { $context->editcalculationurl = $this->gtree->get_edit_calculation_link($element, $this->gpr, $editcalculationstrings); - $object = $element['object']; - if ($object->itemmodule !== 'quiz') { - $context->hideurl = $this->gtree->get_hiding_link($element, $this->gpr, $hidestrings); + if (isset($element['object'])) { + $object = $element['object']; + if ($object->itemmodule !== 'quiz') { + $context->hideurl = $this->gtree->get_hiding_link($element, $this->gpr, $hidestrings); + } } $context->lockurl = $this->gtree->get_locking_link($element, $this->gpr, $lockstrings); } + + // Sorting item. + $sortlink = clone($this->baseurl); + if (isset($element['object']->id)) { + $sortlink->param('sortitemid', $element['object']->id); + } else if ($element['type'] == 'userfield') { + $sortlink->param('sortitemid', $element['name']); + } + + if (($element['type'] == 'userfield') && ($element['name'] == 'fullname')) { + $sortlink->param('sortitemid', 'firstname'); + $context->ascendingfirstnameurl = $this->gtree->get_sorting_link($sortlink, $this->gpr, $titleasc); + $context->descendingfirstnameurl = $this->gtree->get_sorting_link($sortlink, $this->gpr, $titledesc, 'desc'); + + $sortlink->param('sortitemid', 'lastname'); + $context->ascendinglastnameurl = $this->gtree->get_sorting_link($sortlink, $this->gpr, $titleasc); + $context->descendinglastnameurl = $this->gtree->get_sorting_link($sortlink, $this->gpr, $titledesc, 'desc'); + } else { + $context->ascendingurl = $this->gtree->get_sorting_link($sortlink, $this->gpr, $titleasc); + $context->descendingurl = $this->gtree->get_sorting_link($sortlink, $this->gpr, $titledesc, 'desc'); + } + } else if ($element['type'] == 'category') { $categoryid = $element['object']->id; @@ -1705,7 +1749,7 @@ class grade_report_grader extends grade_report { $this->gtree->get_category_view_mode_link($url, $strswitchwhole, 'switch_whole', $fullmode); if (!empty($USER->editing)) { - $context->divider = true; + $context->divider1 = true; $context->editurl = $this->gtree->get_edit_link($element, $this->gpr, $editstrings); $context->hideurl = $this->gtree->get_hiding_link($element, $this->gpr, $hidestrings); $context->lockurl = $this->gtree->get_locking_link($element, $this->gpr, $lockstrings); @@ -1713,7 +1757,11 @@ class grade_report_grader extends grade_report { } - $context->dataid = $element['object']->id; + if (isset($element['object'])) { + $context->dataid = $element['object']->id; + } else if ($element['type'] == 'userfield') { + $context->dataid = $element['name']; + } } else if ($mode == 'user') { foreach ($this->get_report_links($this->context, $this->courseid, $element, $this->gpr, $mode) as $count => $reportlink) { @@ -1724,7 +1772,8 @@ class grade_report_grader extends grade_report { } if (!empty($USER->editing) || isset($context->gradeanalysisurl) || isset($context->gradesonlyurl) - || isset($context->aggregatesonlyurl) || isset($context->fullmodeurl) || isset($context->reporturl0)) { + || isset($context->aggregatesonlyurl) || isset($context->fullmodeurl) || isset($context->reporturl0) + || isset($context->ascendingurl) || isset($context->ascendingfirstnameurl)) { return $OUTPUT->render_from_template('gradereport_grader/cellmenu', $context); } return ''; @@ -1942,13 +1991,9 @@ class grade_report_grader extends grade_report { * @return array An associative array of HTML sorting links+arrows */ public function get_sort_arrows(array $extrafields = array()) { - global $OUTPUT, $CFG; + global $CFG; $arrows = array(); - - $strsortasc = $this->get_lang_string('sortasc', 'grades'); - $strsortdesc = $this->get_lang_string('sortdesc', 'grades'); - $iconasc = $OUTPUT->pix_icon('t/sort_asc', $strsortasc, '', array('class' => 'iconsmall sorticon')); - $icondesc = $OUTPUT->pix_icon('t/sort_desc', $strsortdesc, '', array('class' => 'iconsmall sorticon')); + $sortlink = clone($this->baseurl); // Sourced from tablelib.php // Check the full name display for sortable fields. @@ -1970,7 +2015,13 @@ class grade_report_grader extends grade_report { new moodle_url($this->baseurl, array('sortitemid' => $name)), $this->get_lang_string($name) ); if ($this->sortitemid == $name) { - $arrows['studentname'] .= $this->sortorder == 'ASC' ? $iconasc : $icondesc; + $sortlink->param('sortitemid', $name); + if ($this->sortorder == 'ASC') { + $sorticon = $this->get_sort_arrow('down', $sortlink); + } else { + $sorticon = $this->get_sort_arrow('up', $sortlink); + } + $arrows['studentname'] .= $sorticon; } $arrows['studentname'] .= ' / '; } @@ -1984,11 +2035,14 @@ class grade_report_grader extends grade_report { $arrows[$field] = $fieldlink; if ($field == $this->sortitemid) { + $sortlink->param('sortitemid', $field); + if ($this->sortorder == 'ASC') { - $arrows[$field] .= $iconasc; + $sorticon = $this->get_sort_arrow('down', $sortlink); } else { - $arrows[$field] .= $icondesc; + $sorticon = $this->get_sort_arrow('up', $sortlink); } + $arrows[$field] .= $sorticon; } } diff --git a/grade/report/grader/styles.css b/grade/report/grader/styles.css index c8e1435eed9..a68953d2a79 100644 --- a/grade/report/grader/styles.css +++ b/grade/report/grader/styles.css @@ -174,7 +174,9 @@ } .path-grade-report-grader .gradeparent .sorticon { - margin-left: 3px; + margin-left: 5px; + vertical-align: middle; + margin-right: 1px; } .path-grade-report-grader .gradeparent .gradevalue { @@ -214,10 +216,6 @@ text-align: center; } -.path-grade-report-grader .action-menu { - padding-left: 10px; -} - .path-grade-report-grader .dropdown-menu { width: max-content; } diff --git a/grade/report/grader/templates/cellmenu.mustache b/grade/report/grader/templates/cellmenu.mustache index dec041c4a37..174956bd9f6 100644 --- a/grade/report/grader/templates/cellmenu.mustache +++ b/grade/report/grader/templates/cellmenu.mustache @@ -23,7 +23,15 @@ "editurl": "Edit grade", "hideurl": "Hide", "reporturl0": "Single view for this user", - "reporturl1": "User report" + "reporturl1": "User report", + "ascendingurl": "Ascending", + "descendingurl": "Descending", + "ascendingfirstnameurl": "Ascending", + "descendingfirstnameurl": "Descending", + "ascendinglastnameurl": "Ascending", + "descendinglastnameurl": "Descending", + "divider1": "true", + "divider2": "true" } }}
@@ -43,9 +51,25 @@ {{#gradesonlyurl}}{{{gradesonlyurl}}}{{/gradesonlyurl}} {{#aggregatesonlyurl}}{{{aggregatesonlyurl}}}{{/aggregatesonlyurl}} {{#fullmodeurl}}{{{fullmodeurl}}}{{/fullmodeurl}} - {{#divider}} + {{#advancedgradingurl}}{{{advancedgradingurl}}}{{/advancedgradingurl}} + {{#divider1}} - {{/divider}} + {{/divider1}} + {{#ascendingfirstnameurl}} + + {{{ascendingfirstnameurl}}} + {{{descendingfirstnameurl}}} + + {{{ascendinglastnameurl}}} + {{{descendinglastnameurl}}} + {{/ascendingfirstnameurl}} + {{#ascendingurl}} + {{{ascendingurl}}} + {{{descendingurl}}} + {{/ascendingurl}} + {{#divider2}} + + {{/divider2}} {{#hideurl}}{{{hideurl}}}{{/hideurl}} {{#lockurl}}{{{lockurl}}}{{/lockurl}}
diff --git a/grade/report/grader/tests/behat/behat_gradereport_grader.php b/grade/report/grader/tests/behat/behat_gradereport_grader.php index b3e93e8a88a..5994bc5f57c 100644 --- a/grade/report/grader/tests/behat/behat_gradereport_grader.php +++ b/grade/report/grader/tests/behat/behat_gradereport_grader.php @@ -242,4 +242,16 @@ class behat_gradereport_grader extends behat_base { return "//table[@id='user-grades']//*[@data-id='" . $userid . "']"; } + /** + * Clicks on given user profile field menu. + * + * @Given /^I click on user profile field menu "([^"]*)"$/ + * @param string $field + */ + public function i_click_on_user_profile_field_menu(string $field) { + + $xpath = "//table[@id='user-grades']//*[@data-id='" . $field . "']"; + $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element")); + } + } diff --git a/grade/report/grader/tests/behat/sort_gradeitems_user_fields.feature b/grade/report/grader/tests/behat/sort_gradeitems_user_fields.feature new file mode 100644 index 00000000000..ea0a4f8f925 --- /dev/null +++ b/grade/report/grader/tests/behat/sort_gradeitems_user_fields.feature @@ -0,0 +1,97 @@ +@gradereport @gradereport_grader +Feature: We can sort grades/user fields on the grader report + In order to manage grades on grader report + As a teacher + I need to be able to sort grades or user fields. + + Background: + Given the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | topics | + And the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | StudentA | 2 | d@example.com | + | student2 | StudentB | 4 | a@example.com | + | student3 | StudentC | 3 | c@example.com | + | student4 | StudentD | 1 | b@example.com | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + | student2 | C1 | student | + | student3 | C1 | student | + | student4 | C1 | student | + And the following "activities" exist: + | activity | course | section | name | intro | assignsubmission_onlinetext_enabled | submissiondrafts | + | assign | C1 | 1 | Test assignment name 1 | Submit your online text | 1 | 0 | + And the following "mod_assign > submissions" exist: + | assign | user | onlinetext | + | Test assignment name 1 | student1 | This is a submission for assignment 1 | + | Test assignment name 1 | student2 | This is a submission for assignment 1 | + | Test assignment name 1 | student3 | This is a submission for assignment 1 | + | Test assignment name 1 | student4 | This is a submission for assignment 1 | + And the following "grade items" exist: + | itemname | grademin | grademax | course | + | Manual grade | 20 | 40 | C1 | + And the following "grade grades" exist: + | gradeitem | user | grade | + | Test assignment name 1 | student1 | 80 | + | Test assignment name 1 | student2 | 40 | + | Test assignment name 1 | student3 | 60 | + And I log in as "teacher1" + + @javascript + Scenario: Sort grades or user fields on grader report + When I am on "Course 1" course homepage with editing mode on + And I navigate to "View > Grader report" in the course gradebook + # Default sorting is lastname ascending. + And "StudentD 1" "table_row" should appear before "StudentA 2" "table_row" + And "StudentA 2" "table_row" should appear before "StudentC 3" "table_row" + And "StudentC 3" "table_row" should appear before "StudentB 4" "table_row" + # Sort by grades in descending order. + And I click on grade item menu "Test assignment name 1" + And I choose "Descending" in the open action menu + And I wait until the page is ready + Then "StudentA 2" "table_row" should appear before "StudentC 3" "table_row" + And "StudentC 3" "table_row" should appear before "StudentB 4" "table_row" + And "StudentB 4" "table_row" should appear before "StudentD 1" "table_row" + # Sort by grades in ascending order. + And I click on grade item menu "Test assignment name 1" + And I choose "Ascending" in the open action menu + And I wait until the page is ready + Then "StudentD 1" "table_row" should appear before "StudentB 4" "table_row" + And "StudentB 4" "table_row" should appear before "StudentC 3" "table_row" + And "StudentC 3" "table_row" should appear before "StudentA 2" "table_row" + # Sort by email in ascending order. + And I click on user profile field menu "email" + And I choose "Ascending" in the open action menu + And I wait until the page is ready + Then "StudentB 4" "table_row" should appear before "StudentD 1" "table_row" + And "StudentD 1" "table_row" should appear before "StudentC 3" "table_row" + And "StudentC 3" "table_row" should appear before "StudentA 2" "table_row" + And I click on user profile field menu "email" + # Sort by email in descending order. + And I choose "Descending" in the open action menu + And I wait until the page is ready + Then "StudentA 2" "table_row" should appear before "StudentC 3" "table_row" + And "StudentC 3" "table_row" should appear before "StudentD 1" "table_row" + And "StudentD 1" "table_row" should appear before "StudentB 4" "table_row" + # Sort by firstname in ascending order. + And I click on "First name" "link" + And I wait until the page is ready + Then "StudentA 2" "table_row" should appear before "StudentB 4" "table_row" + And "StudentB 4" "table_row" should appear before "StudentC 3" "table_row" + And "StudentC 3" "table_row" should appear before "StudentD 1" "table_row" + # Sort by firstname in descending order. + And I click on "First name" "link" + And I wait until the page is ready + Then "StudentD 1" "table_row" should appear before "StudentC 3" "table_row" + And "StudentC 3" "table_row" should appear before "StudentB 4" "table_row" + And "StudentB 4" "table_row" should appear before "StudentA 2" "table_row" + # Sort by lastname in ascending order. + And I click on "Last name" "link" + And I wait until the page is ready + Then "StudentD 1" "table_row" should appear before "StudentA 2" "table_row" + And "StudentA 2" "table_row" should appear before "StudentC 3" "table_row" + And "StudentC 3" "table_row" should appear before "StudentB 4" "table_row" diff --git a/grade/report/lib.php b/grade/report/lib.php index 63c6c913140..58638499335 100644 --- a/grade/report/lib.php +++ b/grade/report/lib.php @@ -443,7 +443,6 @@ abstract class grade_report { $pix = ['up' => 't/sort_desc', 'down' => 't/sort_asc']; $matrix = ['up' => 'desc', 'down' => 'asc']; $strsort = $this->get_lang_string($matrix[$direction], 'moodle'); - $arrow = $OUTPUT->pix_icon($pix[$direction], '', '', ['class' => 'sorticon']); return html_writer::link($sortlink, $arrow, ['title' => $strsort, 'aria-label' => $strsort]); }