MDL-77634 gradereport_singleview: Sticky footer for singleview

Moving Save button, pagination, next and previous link as well as
perpage selector to sticky footer.
This commit is contained in:
Ilya Tregubov 2023-05-17 18:06:13 +08:00
parent 1b1a15a308
commit 807bfe69ae
No known key found for this signature in database
GPG key ID: 0F58186F748E55C1
12 changed files with 281 additions and 43 deletions

View file

@ -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

View file

@ -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);;
}
}

View file

@ -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');
}

View file

@ -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();

View file

@ -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,

View file

@ -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)) {

View file

@ -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;
}

View file

@ -25,4 +25,4 @@
"value": "Save"
}
}}
<input type="{{type}}" value="{{value}}" {{disabled}} class="btn btn-secondary">
<input type="{{type}}" value="{{value}}" {{disabled}} class="btn btn-primary">

View file

@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@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"}
]
}
}}
<div class="col-auto">
<label>
{{#str}}show{{/str}}
<select name="perpage" class="mt-1 custom-select ignoredirty" id="{{uniqid}}">
{{#options}}
<option value="{{value}}" {{#selected}}selected{{/selected}}>{{name}}</option>
{{/options}}
</select>
</label>
</div>
{{#pagingbar}}
<div class="col">
{{{pagingbar}}}
</div>
{{/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}}

View file

@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@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": "<div><div class='reporttable'><div class='tableresponsive'><table class='generaltable' id='singleview-grades'><thead> <tr><th>Name</th><th>User fullname</th><th>Grade</th><th>Actions</th> </tr></thead><tbody></tbody></table></div></div></div>",
"stickyfooter": "<div id='sticky-footer'></div>"
}
}}
<form method="post">
{{{table}}}
{{{stickyfooter}}}
<input type="hidden" name="sesskey" value="{{sesskey}}">
</form>

View file

@ -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}}
<div class="d-flex report-paging">
{{{perpageselect}}}
{{{pager}}}
</div>
{{/perpageselect}}
<div class="d-flex ml-auto">
<div class="d-flex ml-auto mt-2">
{{#previoususer}}
<div class="previous d-flex">
<a href="{{url}}" aria-label="{{#str}} gotopreviousreport, gradereport_singleview {{/str}}">
@ -51,7 +50,7 @@
</div>
{{/previoususer}}
</div>
<div class="d-flex ml-auto">
<div class="d-flex ml-auto mt-2">
{{#nextuser}}
<div class="next d-flex ml-auto">
<a href="{{url}}" aria-label="{{#str}} gotonextreport, gradereport_singleview {{/str}}">

View file

@ -0,0 +1,100 @@
@gradereport @gradereport_singleview @javascript
Feature: Singleview report pagination
In order to consume the content of the report better
As a teacher
I need the report to be paginated
Background:
Given the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And the following "activities" exist:
| activity | course | idnumber | name | intro | grade |
| assign | C1 | a1 | Test assignment one | Submit something! | 300 |
Scenario: Default is used when teachers have no preference yet on singleview report
Given "41" "users" exist with the following data:
| username | student[count] |
| firstname | Student |
| lastname | [count] |
| email | student[count]@example.com |
And "41" "course enrolments" exist with the following data:
| user | student[count] |
| course | C1 |
| role |student |
When I am on the "Course 1" "Course" page logged in as "teacher1"
And I navigate to "View > Grader report" in the course gradebook
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
Then the field "perpage" matches value "20"
# There is also 1 header row.
And I should see "21" node occurrences of type "tr" in the "singleview-grades" "table"
And I should see "3" in the ".stickyfooter .pagination" "css_element"
And I should not see "4" in the ".stickyfooter .pagination" "css_element"
Scenario: Teachers can have their preference for the number of students on singleview report
Given the following "courses" exist:
| fullname | shortname |
| Course 2 | C2 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C2 | editingteacher |
And the following "activities" exist:
| activity | course | idnumber | name | intro | grade |
| assign | C2 | a2 | Test assignment two | Submit something! | 300 |
When I am on the "Course 1" "Course" page logged in as "teacher1"
And I navigate to "View > Grader report" in the course gradebook
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
And I set the field "perpage" to "100"
And I am on the "Course 2" "Course" page
And I navigate to "View > Grader report" in the course gradebook
And I click on grade item menu "Test assignment two" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
Then the field "perpage" matches value "100"
Scenario: Teachers can change the number of students shown on singleview report
Given "101" "users" exist with the following data:
| username | student[count] |
| firstname | Student |
| lastname | [count] |
| email | student[count]@example.com |
And "101" "course enrolments" exist with the following data:
| user | student[count] |
| course | C1 |
| role |student |
When I am on the "Course 1" "Course" page logged in as "teacher1"
And I navigate to "View > Grader report" in the course gradebook
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
And I set the field "perpage" to "100"
# There is also 1 header row.
Then I should see "101" node occurrences of type "tr" in the "singleview-grades" "table"
And I should see "2" in the ".stickyfooter .pagination" "css_element"
And I should not see "3" in the ".stickyfooter .pagination" "css_element"
@javascript
Scenario: The pagination bar is only displayed when there is more than one page on singleview report
Given "21" "users" exist with the following data:
| username | student[count] |
| firstname | Student |
| lastname | [count] |
| email | student[count]@example.com |
And "21" "course enrolments" exist with the following data:
| user | student[count] |
| course | C1 |
| role |student |
When I am on the "Course 1" "Course" page logged in as "teacher1"
And I navigate to "View > Grader report" in the course gradebook
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
# By default, we have 20 students per page.
And ".stickyfooter .pagination" "css_element" should exist
And I set the field "perpage" to "100"
Then ".stickyfooter .pagination" "css_element" should not exist