From 807bfe69ae9d24e0b3f53dfd7032ff6218dca8cf Mon Sep 17 00:00:00 2001 From: Ilya Tregubov Date: Wed, 17 May 2023 18:06:13 +0800 Subject: [PATCH] MDL-77634 gradereport_singleview: Sticky footer for singleview Moving Save button, pagination, next and previous link as well as perpage selector to sticky footer. --- .../singleview/classes/local/screen/grade.php | 10 ++ .../classes/local/screen/screen.php | 50 ++++++--- .../classes/local/screen/tablelike.php | 13 +-- .../singleview/classes/local/screen/user.php | 10 ++ grade/report/singleview/index.php | 18 +++- grade/report/singleview/renderer.php | 1 - grade/report/singleview/styles.css | 5 - .../singleview/templates/button.mustache | 2 +- .../singleview/templates/perpage.mustache | 58 ++++++++++ .../singleview/templates/report.mustache | 38 +++++++ .../templates/report_navigation.mustache | 19 ++-- .../singleview/tests/behat/pagination.feature | 100 ++++++++++++++++++ 12 files changed, 281 insertions(+), 43 deletions(-) create mode 100644 grade/report/singleview/templates/perpage.mustache create mode 100644 grade/report/singleview/templates/report.mustache create mode 100644 grade/report/singleview/tests/behat/pagination.feature diff --git a/grade/report/singleview/classes/local/screen/grade.php b/grade/report/singleview/classes/local/screen/grade.php index 25d51669ffc..cccef5ca812 100644 --- a/grade/report/singleview/classes/local/screen/grade.php +++ b/grade/report/singleview/classes/local/screen/grade.php @@ -158,6 +158,16 @@ class grade extends tablelike implements selectable_items, filterable_items { return; } + // If we change perpage on pagination we might end up with a page that doesn't exist. + if ($this->perpage) { + $numpages = intval($this->totalitemcount / $this->perpage) + 1; + if ($numpages <= $this->page) { + $this->page = 0; + } + } else { + $this->page = 0; + } + $params = [ 'id' => $this->itemid, 'courseid' => $this->courseid diff --git a/grade/report/singleview/classes/local/screen/screen.php b/grade/report/singleview/classes/local/screen/screen.php index 1ef3ab51d50..47c16c839aa 100644 --- a/grade/report/singleview/classes/local/screen/screen.php +++ b/grade/report/singleview/classes/local/screen/screen.php @@ -85,11 +85,14 @@ abstract class screen { */ protected $items; + /** @var int Maximum number of students that can be shown on one page */ + protected static $maxperpage = 5000; + /** * List of allowed values for 'perpage' setting * @var array $validperpage */ - protected static $validperpage = [20, 50, 100, 200, 400, 1000, 5000]; + protected static $validperpage = [20, 100]; /** * To store course data @@ -124,17 +127,19 @@ abstract class screen { $cache = \cache::make_from_params(\cache_store::MODE_SESSION, 'gradereport_singleview', 'perpage'); $perpage = optional_param('perpage', null, PARAM_INT); - if (!in_array($perpage, self::$validperpage)) { + if (!in_array($perpage, self::$validperpage) && ($perpage !== 0)) { // Get from cache. $perpage = $cache->get(get_class($this)); } else { // Save to cache. $cache->set(get_class($this), $perpage); } - if ($perpage) { + if (isset($perpage) && $perpage) { $this->perpage = $perpage; } else { - $this->perpage = 100; + // Get from cache. + $perpage = $cache->get(get_class($this)); + $this->perpage = ($perpage === 0) ? $perpage : min(self::$validperpage); } $this->init(empty($itemid)); @@ -423,16 +428,37 @@ abstract class screen { public function perpage_select(): string { global $PAGE, $OUTPUT; - $options = array_combine(self::$validperpage, self::$validperpage); - $url = new moodle_url($PAGE->url); - $url->remove_params(['page', 'perpage']); + $numusers = count($this->items); + // Print per-page dropdown. + $pagingoptions = self::$validperpage; + if ($this->perpage) { + $pagingoptions[] = $this->perpage; // To make sure the current preference is within the options. + } + $pagingoptions = array_unique($pagingoptions); + sort($pagingoptions); + $pagingoptions = array_combine($pagingoptions, $pagingoptions); + if ($numusers > self::$maxperpage) { + $pagingoptions['0'] = self::$maxperpage; + } else { + $pagingoptions['0'] = get_string('all'); + } - $out = ''; - $select = new \single_select($url, 'perpage', $options, $this->perpage, null, 'perpagechanger'); - $select->label = get_string('itemsperpage', 'gradereport_singleview'); - $out .= $OUTPUT->render($select); + $perpagedata = [ + 'baseurl' => $url->out(false), + 'options' => [] + ]; + foreach ($pagingoptions as $key => $name) { + $perpagedata['options'][] = [ + 'name' => $name, + 'value' => $key, + 'selected' => $key == $this->perpage, + ]; + } - return $out; + // The number of students per page is always limited even if it is claimed to be unlimited. + $this->perpage = $this->perpage ?: self::$maxperpage; + $perpagedata['pagingbar'] = $this->pager(); + return $OUTPUT->render_from_template('gradereport_singleview/perpage', $perpagedata);; } } diff --git a/grade/report/singleview/classes/local/screen/tablelike.php b/grade/report/singleview/classes/local/screen/tablelike.php index 4c408a78307..bd82fd7ae4f 100644 --- a/grade/report/singleview/classes/local/screen/tablelike.php +++ b/grade/report/singleview/classes/local/screen/tablelike.php @@ -194,6 +194,7 @@ abstract class tablelike extends screen implements be_readonly { return $warnings; } $table = new html_table(); + $table->id = 'singleview-grades'; $table->head = $this->headers(); @@ -219,17 +220,7 @@ abstract class tablelike extends screen implements be_readonly { $data->table = $table; $data->instance = $this; - $buttonattr = ['class' => 'singleview_buttons submit']; - $buttonhtml = implode(' ', $this->buttons($this->is_readonly())); - $buttons = html_writer::tag('div', $buttonhtml, $buttonattr); - - $sessionvalidation = html_writer::empty_tag('input', - ['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]); - - $html = html_writer::tag('form', - html_writer::table($table) . $this->bulk_insert() . $buttons . $sessionvalidation, - ['method' => 'POST'] - ); + $html = html_writer::table($table); return html_writer::div($html, 'reporttable'); } diff --git a/grade/report/singleview/classes/local/screen/user.php b/grade/report/singleview/classes/local/screen/user.php index 782e01f16fc..2cdbd92b2d2 100644 --- a/grade/report/singleview/classes/local/screen/user.php +++ b/grade/report/singleview/classes/local/screen/user.php @@ -121,6 +121,16 @@ class user extends tablelike implements selectable_items { } } + // If we change perpage on pagination we might end up with a page that doesn't exist. + if ($this->perpage) { + $numpages = intval(count($this->items) / $this->perpage) + 1; + if ($numpages <= $this->page) { + $this->page = 0; + } + } else { + $this->page = 0; + } + $this->requirespaging = count($this->items) > $this->perpage; $this->setup_structure(); diff --git a/grade/report/singleview/index.php b/grade/report/singleview/index.php index 4569e3cbf51..b8dfb2b2d70 100644 --- a/grade/report/singleview/index.php +++ b/grade/report/singleview/index.php @@ -36,7 +36,7 @@ $userid = optional_param('userid', null, PARAM_INT); $itemid = optional_param('itemid', null, PARAM_INT); $itemtype = optional_param('item', null, PARAM_TEXT); $page = optional_param('page', 0, PARAM_INT); -$perpage = optional_param('perpage', 100, PARAM_INT); +$perpage = optional_param('perpage', null, PARAM_INT); $edit = optional_param('edit', -1, PARAM_BOOL); // Sticky editing mode. @@ -203,13 +203,13 @@ if ($data = data_submitted()) { // Make sure we have proper final grades. grade_regrade_final_grades_if_required($course); -echo $report->output(); // Save the screen state in a session variable as last viewed state. $SESSION->gradereport_singleview["itemtype-{$context->id}"] = $itemtype; if ($itemid) { $SESSION->gradereport_singleview["{$itemtype}item-{$context->id}"] = $itemid; } +$stickyfooter = ''; if (($itemtype !== 'select') && ($itemtype !== 'grade_select') &&($itemtype !== 'user_select')) { $item = (isset($userid)) ? $userid : $itemid; @@ -223,9 +223,21 @@ if (($itemtype !== 'select') && ($itemtype !== 'grade_select') &&($itemtype !== $userreportrenderer = $PAGE->get_renderer('gradereport_singleview'); // Add previous/next user navigation. - echo $userreportrenderer->report_navigation($gpr, $courseid, $context, $report, $groupid, $itemtype, $itemid); + $footercontent = $userreportrenderer->report_navigation($gpr, $courseid, $context, $report, $groupid, $itemtype, $itemid); + + $buttonhtml = implode(' ', $report->screen->buttons($report->screen->is_readonly())); + $footercontent .= $report->screen->bulk_insert() . $buttonhtml; + + $stickyfooter = new core\output\sticky_footer($footercontent); + $stickyfooter = $OUTPUT->render($stickyfooter); + } +echo $OUTPUT->render_from_template('gradereport_singleview/report', [ + 'table' => $report->output(), + 'stickyfooter' => $stickyfooter, + 'sesskey' => sesskey() +]); $event = \gradereport_singleview\event\grade_report_viewed::create( [ 'context' => $context, diff --git a/grade/report/singleview/renderer.php b/grade/report/singleview/renderer.php index c4fd5d7638d..9bf1f5afb44 100644 --- a/grade/report/singleview/renderer.php +++ b/grade/report/singleview/renderer.php @@ -152,7 +152,6 @@ class gradereport_singleview_renderer extends plugin_renderer_base { if ($report->screen->supports_paging()) { $navigationdata['perpageselect'] = $report->screen->perpage_select(); - $navigationdata['pager'] = $report->screen->pager(); } if (isset($navigationdata)) { diff --git a/grade/report/singleview/styles.css b/grade/report/singleview/styles.css index 9d02f09f702..7623fd2e516 100644 --- a/grade/report/singleview/styles.css +++ b/grade/report/singleview/styles.css @@ -6,7 +6,6 @@ } .path-grade-report-singleview div.groupselector, -.path-grade-report-singleview div.reporttable form div.singleview_buttons, .path-grade-report-singleview div.selectitems { display: block; text-align: right; @@ -30,10 +29,6 @@ text-align: left; } -.path-grade-report-singleview .singleview_buttons { - padding: 10px 0; -} - .path-grade-report-singleview div.reporttable h2 { text-align: left; } diff --git a/grade/report/singleview/templates/button.mustache b/grade/report/singleview/templates/button.mustache index 5252ab5a4ca..12dc2d90f34 100644 --- a/grade/report/singleview/templates/button.mustache +++ b/grade/report/singleview/templates/button.mustache @@ -25,4 +25,4 @@ "value": "Save" } }} - + diff --git a/grade/report/singleview/templates/perpage.mustache b/grade/report/singleview/templates/perpage.mustache new file mode 100644 index 00000000000..d74cf73a45a --- /dev/null +++ b/grade/report/singleview/templates/perpage.mustache @@ -0,0 +1,58 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template gradereport_grader/perpage + + The students per page dropdown element. + + Context variables required for this template: + * baseurl - The base URL for the perpage action. + * options - Choices + + Example context (json): + { + "baseurl": "http://example.com/grade/report/grader/index.php?id=2&report=grader", + "options": [ + {"name": "20", "value": "20"}, + {"name": "100", "value": "100", "selected": true}, + {"name": "All", "value": "0"} + ] + } +}} +
+ +
+{{#pagingbar}} +
+ {{{pagingbar}}} +
+{{/pagingbar}} +{{#js}} + document.getElementById('{{uniqid}}').addEventListener('change', function(e) { + var url = new URL('{{{baseurl}}}'); + url.searchParams.set('perpage', e.target.value); + + window.location.href = url; + }); +{{/js}} diff --git a/grade/report/singleview/templates/report.mustache b/grade/report/singleview/templates/report.mustache new file mode 100644 index 00000000000..51982ead62d --- /dev/null +++ b/grade/report/singleview/templates/report.mustache @@ -0,0 +1,38 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template gradereport_singleview/report + + Report table and sticky footer. + + Context variables required for this template: + * sesskey - string - The session key. + * table - string - HTML content of the grade items or users table. + * stickyfooter - string - HTML content of the sticky footer. + + Example context (json): + { + "sesskey": "fakesesskey", + "table": "
NameUser fullnameGradeActions
", + "stickyfooter": "" + } +}} +
+ {{{table}}} + {{{stickyfooter}}} + +
diff --git a/grade/report/singleview/templates/report_navigation.mustache b/grade/report/singleview/templates/report_navigation.mustache index 5973f09e640..d7a4b9514e0 100644 --- a/grade/report/singleview/templates/report_navigation.mustache +++ b/grade/report/singleview/templates/report_navigation.mustache @@ -13,23 +13,23 @@ }} {{! @template gradereport_singleview/report_navigation - The previous/next user navigation for the user report view. + The previous/next user/item navigation for the singleview report. Context variables required for this template: * previoususer - (optional) The object containing information about the previous user. - * name - The name of the previous user. - * url - The URL to the previous user report. + * name - The name of the previous user/item. + * url - The URL to the previous user/item report. * nextuser - (optional) The object containing information about the next user. - * name - The name of the next user. - * url - The URL to the next user report. + * name - The name of the next user/item. + * url - The URL to the next user/item report. Example context (json): { "previoususer": { "name": "John Smith", - "url": "https://example.com/grade/report/user/index.php?id=2&userid=3" + "url": "https://example.com/grade/report/singleview/index.php?id=2&itemid=3" }, "previoususer": { "name": "Jane Doe", - "url": "https://example.com/grade/report/user/index.php?id=2&userid=5" + "url": "https://example.com/grade/report/singleview/index.php?id=2&itemid=5" } } }} @@ -38,10 +38,9 @@ {{#perpageselect}}
{{{perpageselect}}} - {{{pager}}}
{{/perpageselect}} -
+ -
+
{{#nextuser}}